Wednesday, March 21, 2012

Task-based Asynchronous Programming in .NET 4.5

.NET 4.5 introduces the new "async" and "await" keywords to simplify the asynchronous programming. They are task-based asynchronous model, and are extensively used in the new Metro applications for better responsive UI experience. Following code example illustrate the simple usage of "async" and "await" (run in Visual Studio 11 Beta):
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Net;
using System.Net.Http;
using System.IO;
using System.Diagnostics;

namespace ConsoleApplication1
{
    class Program
    {
        static void Main(string[] args)
        {
            StringBuilder syncContent = new StringBuilder(), asyncContent = new StringBuilder();
            Stopwatch watch = new Stopwatch();
            string testUrl = "http://www.google.com";
            GetWebContent(testUrl); // Warming up network connection

            Console.WriteLine("Sync web request calls start...");
            watch.Start();
            for (int i = 0; i < 100; i++)
            {
                syncContent.Append(GetWebContent(testUrl));
            }
            watch.Stop();
            Console.WriteLine("Sync web calls completed within {0} ms", watch.ElapsedMilliseconds);
            Console.WriteLine("Sync web content lengh: {0}", syncContent.ToString().Length);

            List<Task<string>> tasks = new List<Task<string>>();
            Console.WriteLine("\nSync web request calls start...");
            watch.Restart();
            for (int i = 0; i < 100; i++)
            {
                tasks.Add(GetWebContentAsync(testUrl));
            }
            watch.Stop();

            Console.WriteLine("Async web calls returned within {0} ms", watch.ElapsedMilliseconds);
            watch.Restart();
            Task.WaitAll(tasks.ToArray());
            watch.Stop();
            tasks.ForEach(v => asyncContent.Append(v.Result));
            Console.WriteLine("ASync web calls completed within {0} ms", watch.ElapsedMilliseconds);
            Console.WriteLine("Async web content lengh: {0}", asyncContent.Length);

            Console.Read();
        }

        static async Task<string> GetWebContentAsync(string url)
        {
            var webClient = new WebClient();
            var content = await webClient.DownloadStringTaskAsync(url);
            return content;
        }

        static string GetWebContent(string url)
        {
            var webClient = new WebClient();
            var content = webClient.DownloadString(url);
            return content;
        }
    }
}
The synchronous method is included in the demo cod for comparison purpose. The console app result:



Basically you need to add "async" keyword to the function definition telling compiler they could be asynchronous call(s) inside that method; "async" methods can return Task (no actual return value), Task<T> (return value of T) or void (only used in event handler without any return).

Inside a method marked as "async", you are able to use "await" keyword for a call that promises return or return value. Once "await" keyword is used, compiler will do some magic stuff for you and make that asynchronous invocation happen. Many IO and network related methods in .NET 4.5 have been refactored to support this task-based asynchronous model. Those asynchronous methods, with naming convention of xxxxAsync where xxxx is the method name, are out-of-box and can be used directly with "await" keyword.

This async programming model is much easier to work with comparing to the old .NET asynchronous programming model. In old days, developers need to spend a lot of effort to handle those callback functions such as Beginxxxx/Endxxxx methods and IAsyncResult parameter. That's painful and sometimes it's impossible to do nested async calls and error handling. With the new task-based async model, all those hustles are gone.

You can easily wrap a method as async method using TPL introduced in .NET 4.0. Code snippet below shows one way to achieve this:
        static async Task<double> MyComplicatedHeavyCalculationAsync(double x, double y)
        {
            double value = await Task<double>.Run(() => MyComplicatedHeavyCalculation(x, y));
            return value;
        }

        static double MyComplicatedHeavyCalculation(double x, double y)
        {
            // Do your complicated work ...
            return x + y;
        }
All sound good. But how about the thread context such as updating the UI content? In WPF, Silverlight and Metro apps you are most likely safe to do so since compiler are smart enough to inject the delegate code that could marshall the update back to the UI thread. For ASP.NET things are a bit more tricky. You have thread pool and httpcontext for each request is dynamically generated. Good news is that the new ASP.NET 4.5 will support task-based asynchronous model in certain ways, refer next version ASP.NET white paper for detail.