Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

What happens to an asynchronous WCF call when its client disposes or goes out of scope?

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?

like image 627
SimonGoldstone Avatar asked Sep 25 '13 09:09

SimonGoldstone


2 Answers

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!

like image 115
SimonGoldstone Avatar answered Oct 08 '22 19:10

SimonGoldstone


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.

like image 43
svick Avatar answered Oct 08 '22 21:10

svick