I am maintaining some code which has a class containing a method that calls a WCF method asynchronously using the Task pattern.
The code effectively looks like this:
public class Manager : IDisposable
{
public void DoSomething()
{
Task<bool> task;
using (var client = new WcfClient())
{
task = client.ReallyDoSomethingAsync(123);
}
}
}
The manager itself is used somewhere else in another piece of code that wraps the call to DoSomething inside a using(Manager) block.
So my question is, what happens to the WCF call. Does it happen? Does it abandon?
And more generally, does this hold true for asynchronous calls using the Task<T>
pattern? What happens if the owning class goes out of scope before the asynchronous call finishes?
After knocking-up a brief test app, it seems that when a WCF client is disposed, it waits for all async tasks to complete before it actually disposes.
Example WCF service:
public class Service1 : IService1
{
public string GetData(int value)
{
Thread.Sleep(5000);
return "GOT HERE " + value;
}
}
Example client:
class Program
{
static void Main(string[] args)
{
using (var wrapper = new Wrapper())
{
wrapper.DoSomething();
}
Console.WriteLine("Finished.");
Console.ReadLine();
}
}
class Wrapper : IDisposable
{
public void DoSomething()
{
Task<string> task1;
using (var client = new ServiceReference1.Service1Client())
{
task1 = client.GetDataAsync(1);
var task2 = client.GetDataAsync(2);
Thread.Sleep(1000);
var task3 = client.GetDataAsync(3);
Console.WriteLine("Calls started");
}
Console.WriteLine("Result of task 1:" + task1.Result);
}
public void Dispose()
{
}
}
In this scenario, the "Calls started" line appears after the 1 second delay. The "Finished." line does not get written until all the three tasks have completed successfully.
So it does in fact appear that the WCF Service Client wrapper manages its tasks and waits for completion (or presumably timeout) before disposing.
... Which gets me thinking: If you want to call a long running WCF method in a "fire and forget" asynchronous manner, you would either have to do it without a using
block or wrap the whole thing in its own task. So that's a lot of wrapping!
Basically, each Task
has two sides: the producer (which can be something like TaskCompletionSource
) and the consumer (the Task
itself).
Even when there are no references to the consumer side, there still is a reference to the producer side. What this means is that abandoning the Task
won't do anything: the async call will continue as normal. And only after it completes, the Task
will become eligible for garbage collection.
If you do want to cancel the asynchronous operation, you need to explicitly tell the producer to stop, which is usually done by passing it a CancellationToken
.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With