Tuesday 4 June 2013

Evolution of Asynchronous Programming (EAP) in .NET

Asynchronous programming model in .NET makes it very easy for developers to write event based asynchronous pattern (EAP) code starting .NET version 4.5. We have been trained for long enough by .NET programming practices to use event based pattern for any asynchronous work. For example, you want to perform a long running operation. When calling this long running function, you don’t want your client code to make a blocking call; instead a very famous pattern that many architects suggests here is to ask you client to pass a callback function or delegate and that callback function will be called as soon as this long running operation is over. This is nothing but the EAP pattern.

As shown in following example, function “client” makes a call to “longfunction” and waits for the long function to complete. This is a blocking call.

private Task<string> CallLongRunningService()
{
  var client = new ServiceReference1.Service1Client();

  // blocking call where main thread is blocked
  client.CallLongRunningService();
}


Now instead of just waiting for “longfunction” to complete, “client” function wants to do some other stuff. Here many architects would suggest to use EAP pattern and call the long function asynchronously.
So we refactor “CallLongRunningService” to “CallLongRunningServiceWithCallback” and ask a callback function to be called when long function is complete.

private void CallLongRunningServiceWithCallback()
{
  var client = new ServiceReference1.Service1Client();

  client.LongFunctionCallback += LongFunctionCallback;

  client.RunLongFunctionAsync();
}

private void LongFunctionCallback(CallbackArgs args)
{
  // set the result in some local variable. Basically store the result so that others
  // can access it
  m_result = args.Result;
}

As you can guess “client” function also need to be refactored to consume the refactored “CallLongRunningServiceWithCallback” and reconcile its logic with the callback function. Now we have split the logic into two functions, main function and callback function.

Now this is where TaskCompletionSource and Task Parallel Library TPL comes to rescue. Using TaskCompletionSource you can refactor the above code as follows:


private Task<string> CallLongRunningServiceWithCallback()
{
  var tcs = new TaskCompletionSource<string>();

  var client = new ServiceReference1.Service1Client();

  client.LongFunctionCallback += (o, args) =>
    {
      if (args.Error != null)
      {
        tcs.TrySetException(args.Error);
        return;
      }
      if (args.Cancelled)
      {
        tcs.TrySetCanceled();
        return;
      }

      tcs.TrySetResult(args.Result);
    };

    client.RunLongFunctionAsync();

    // Do some other work…

    return tcs.Task;
}


Client application will get the task as return parameter and will have to call task.Result to get the result from long running function. In the above see the comment line “Do some other work…”. This is where you can do something else while the long function is doing its job. Following is the code you will write to consume the long running function with callback.

Task<string> t = CallLongRunningFunctionWithCallback();

Console.WriteLine(t.Result);


Where is the blocking call here? In above lines of code when you call t.Result it’s a blocking call on current thread. You need this anyway because we don’t know how long the long function will take to complete. But the best thing about this pattern that I allows us to not just wait but we can carry other activities during the function is running. Also see TPL code is much simpler than our earlier when callback was a separate function.  TPL provides TaskCompletionSource to help us track the result of the async task in a consistent way across the projects.