Syntactic Sugar and the Async Pill

18 October 2012
by Dino Esposito

Asynchrony is essential  for scalability and performance on the server side. Although it has always been possible to write asynchronous code, there has, up to now, been a downside: it is difficult to understand and maintain. Now, with the async/await. keywords, the whole approach is radically simplified for the programmer.

If you have read my article “Go Async or Sink”, then you should already be convinced that asynchrony is essential for providing scalability and actual performance for any modern (server-side) software. Any developers would agree that asynchronous software is important yet will do whatever he can to avoid writing async methods. Why? The reason is obvious: writing asynchronous code is boring, annoying and error prone.

For the most part, when working with asynchronous code, the developer is forced to focus on the technical details of the implementation rather than the workflow they’re setting up. I don’t think that any developers would have ever bothered writing a method asynchronously if that method was the only call of a procedure.

If your web site needs to download an RSS feed, for example, it will not make any significant difference to performance whether the operation is coded synchronously or asynchronously. It’s a single operation and, probably, you’re also having a caching layer around results. The chances are that the request will complete in just a few milliseconds. At any rate, coding a single operation with an async pattern doesn’t significantly raise the level of complexity of your code.

It’s a different story when results of multiple network (or file-based) operations are to be retrieved and then combined together through the steps of a workflow. In this case, there’s a stronger business case for coding the logic asynchronously. When implementing a chain of async operations, it is difficult to avoid writing code that is an intricate mess of nested calls and lambdas, not much different from the classic and notorious spaghetti code.

The .NET Framework has always had support for async operation: However, Microsoft devoted much time to trying to make async more digestible to developers. So we first had Async Web Forms page in the .NET Framework 2.0 and then async controllers in ASP.NET MVC 2. We also had several scattered general purpose APIs to deal more effectively with tasks and their combination. What developers really wanted, but never loudly demanded, was a spoonful of syntactic sugar to help the medicine go down. This async pill was given enough sugar only in the latest .NET Framework 4.5.

Go Async as You Would Go Sync

With the release of the .NET Framework 4.5, the main .NET languages, C# and Visual Basic, now feature a couple of new keywords: async and await. The use of these two new keywords in combination radically simplifies the writing of asynchronous code. The most characteristic aspect of async and await keywords is the fact that their use makes apparently synchronous code work asynchronously. Or, from another perspective, it makes asynchronously-working code as easy to read and understand as if it was written to run synchronously.

Of the two keywords, await is the most important in the sense that it is the marker that compilers look for and transform into “classic” async patterns just like the ones you have had to implement yourself up until .NET Framework 4.0. The other keyword, async, is used mostly to identify a method that is making use internally of async functionality. Let’s have a look at some code.

The signature of an async method looks like the following:

public async Task<String> DoSomething()
{
    ...
}

The signature has the async modifier and returns a Task<TResult> object, a Task object or simply void. The method will return a Task<TResult> object if the method is actually expected to return a TResultvalue or Task (or void) if the method is void. It turns out that Task is a kind of wrapper that represents a pending task expected to return void or a value of type TResult.

A method marked async must contain a call to method marked await. If not, the method will be run synchronously. In addition, you can useawait only on method calls that return a Task object. Calling awaiton a method that returns void is not permitted (and doesn’t make sense at all). The point is that await tells the compiler to perform some magic on a task. This task may return a value (Task<T>) or be void (Task) but it needs be something concrete. So a method like below …

public async void DoSomething()
{
    // Must have here a call to an asynchronously running method
    // marked with the await keyword.
}

… is only useful for event handlers or external wrappers of async operations that don’t need to return data to their own callers and are invoked synchronously by their callers.

Await/Async in ASP.NET MVC

Let’s move on and consider what it takes to write asynchronous code in ASP.NET MVC. As mentioned, you have been able to have asynchronous controllers since version 2 of the ASP.NET MVC framework. As we’ve seen in the article “Go Async or Sink”, an async controller needs two physical methods to implement one logical method. The first method serves to start the operation (on a selected thread); the second method serves to complete the operation and generates the action result for the ASP.NET MVC runtime. In ASP.NET MVC 4 you need the following:

public class HomeController : AsyncController
{
    private readonly HomeOrchestrator _service;
    public HomeController()
    {
        _service = new HomeOrchestrator();
    }

    public ActionResult Index()
    {
        return View();
    }

    public async Task<ActionResult> Demo1()
    {
        var model = await _service.GetDataForIndexScreen();
        return View(model);
    }
}

As expected, the controller class inherits from AsyncController and can contain synchronous and asynchronous methods. Demo1 is clearly the asynchronous method—it is marked with asyncand returns Task<ActionResult>. The implementation of the method is centered on a worker service as discussed in another article of mine “Never Mind the Controller, Here is the Orchestrator”. The real asynchronous job is done by the GetDataForIndexScreen method which is expected to make calls around and collect and format data properly. The execution of the method is controlled by await meaning that the actual execution is split in two parts as it was the case in earlier versions of ASP.NET MVC. Let’s turn our attention to the body of the GetDataForIndexScreen method.

public class HomeOrchestrator
{
    public async Task<HomeIndexModel> GetDataForIndexScreen()
    {
        const String rssUrl = "...";
        var client = new HttpClient();
        var rss = await client.GetStringAsync(rssUrl);
        var model = new HomeIndexModel();
        model.News = ParseRssInternal(rss);
        return model;
    }

    private IList<String> ParseRssInternal(String rss)
    {
        var items = GetItems(rss);
        return (from item in items select item.Title.Text).ToList();
    }

    // Requires a reference to the System.ServiceModel assembly
    private IList<SyndicationItem> GetItems(String xml)
    {
        var textReader = new StringReader(xml);
        var xmlReader = XmlReader.Create(textReader);
        var feed = SyndicationFeed.Load(xmlReader);
        return feed == null ? null : new List<SyndicationItem>(feed.Items);
    }
}

Besides the internal helper methods to manipulate RSS, the core of the code revolves around the role of the new HttpClient class. Introduced with the .NET Framework 4.5 this class exists specifically to support asynchronous code through await/async and the related new Task-oriented pattern. As we’ll see in a moment, in fact, the biggest benefit of usingHttpClient and async/await is particularly apparent when it comes to manage and coordinate multiple pending requests.

As a side note, consider also that HttpClient is not limited to async/await. According to Microsoft there are several other interesting reasons to prefer HttpClient over WebClient and HttpWebRequest. You can read more here: http://blogs.msdn.com/b/henrikn/archive/2012/02/16/httpclient-is-here.aspx.

HttpClient offers a method GetStringAsync through which you can download text from a remote URL. For developers who are used to WebClient, this method is analogous to DownloadStringAsync. HttpClient also offers a few other methods ending with the Async suffix—GetAsync, GetStreamAsync plus a variety of Put-style methods.

Some classes of the .NET Framework have also been updated to include new Async methods that work well with the async/await pattern. It should be noted that not just any method with an Async suffix works with the task-based pattern necessary for using async/await keywords. Check the documentation carefully before you leap. The task-pattern for async/await is .NET Framework 4.5 only.

The Magic of Await

How does the spoonful of sugar help the medicine go down? Or rather what happens under the hood of the await keyword?

When the C# (or Visual Basic) compiler meets the await keyword it understands that the current method—necessarily one marked as async—can’t continue past that point until the method following the await keyword has completed. This is precisely the behavior one would expect in plain old synchronous code—except that the combination of async/await keywords make it happen asynchronously! It’s the compiler, not you, to take care of all the annoying and fairly sophisticated code that is necessary here. And again complexity is not given by a single standalone call, but comes when you need to coordinate multiple calls.

The compiler replaces await with some code that sets up a special object—called the awaiter—to wait for the completion of the awaited operation. The generated code keeps track of location where execution was suspended and takes a note of where to jump once the awaited operation has terminated. The generated code yields to the caller of the awaited method so that execution can proceed if there’s any code up the stack that can be executed while we wait for the completion of the operation.

In the context of ASP.NET MVC this means that execution first encounters an await call in the controller method Demo1. This creates a first awaiter for GetDataForIndexScreen. Internally,GetDataForIndexScreen creates a second awaiter to wait for the download of the RSS data. Both blocks centered on the await keyword return to respective caller meaning the execution bubbles up from the controller class and returns to the action invoker—part of the ASP.NET MVC machinery. The action invoker on the AsyncController base class knows what to do and does what’s in its power to split the processing of the request in two part freeing the current ASP.NET thread.

At some point—asynchronously—the RSS download ends. The innermost awaiter (the one in GetDataForIndexScreen) resumes execution from the line past the await keyword. The RSS feed is parsed, the view model object is filled and returned. This signals the outermost awaiter—the one in the controller method. At this point execution ends in the controller and bubbles up to the action invoker. In turn, the invoker manages to have the ASP.NET request completed through a currently available thread in the pool. (See Figure 1.)

From the action invoker down to innermost awaiter and back

FIGURE 1. From the action invoker down to innermost awaiter and back.

Coordinating Tasks

So far we considered the simplest scenario when a single potentially lengthy task is run asynchronously. What if you need to retrieve data from, say, two or more RSS feeds? You probably want to run these tasks in parallel. Here’s how you do that with async/await.

public async Task<HomeIndexModel> GetDataForIndexScreen()
{
    var client = new HttpClient();
    var step1 = client.GetStringAsync(...);
    var step2 = client.GetStringAsync(...);

    // Assuming that return types from operations are HOMOGENEOUS
    var results = await Task.WhenAll(step1, step2);

    var model = new HomeIndexModel();
    model.News1 = ParseRssInternal(results[0]);
    model.News2 = ParseRssInternal(results[1]);
    return model;
}

You define two or more Task objects by simply saving the return value of awaitable methods. Variables step1 and step2 are of type Task<String> as String is the type returned by GetStringAsync. Awaiting on the method Task.WhenAll has the effect of resuming execution only when both tasks completed—either successfully or not. Variable results is Task<TResult[]> whereTResult is the type returned by both operations. If operations return non homogeneous data, or one is just void, you proceed as follows:

public async Task<HomeIndexModel> GetDataForIndexScreen(){ var client = new HttpClient(); var step1 = client.GetStringAsync(...); var step2 = client.GetAsync(...); await Task.WhenAll(step1, step2); var model = new HomeIndexModel(); // As return types are not homogeneous need to acquire results separately. // However, as the operation is COMPLETED, await(s) below return immediately. model.News = ParseRssInternal(await step1); model.Data = ParseRssInternal(await step2); return model;}

As you can see, await is used twice, but the second time it is used past operations completed. In this case, the code emitted by the compiler doesn’t cause a suspension but returns immediately.

Finally, there’s the scenario when the results of a previous operation should be used in a subsequent step. You code it in the same way you would do in a synchronous world, except that you wrap it up in an async method and use await modifiers for any step:

// First get a list of URL to process from a remote URL
var listOfUrls = await client.GetStringAsync(url_to_get_the_list);

// Next, loop over the list and process URLs separately
foreach(var url in listOfUrls) {
    var news = await client.GetStringAsync(url);
    :
}

One important note: if you use IntelliSense on Task you find two similar looking methods: WaitAll and WhenAll. Only the second works with the async/await pattern. The former stops execution and waits but doesn’t free up the thread. Put another way, only WhenAllis truly asynchronous and leverages the magic of the newest compilers.

Summary

Asynchrony becomes very important when you have to set up complex workflows that consist of several chained/parallel steps. How many times did you say “if only I could code this synchronously as I always did…”. Now this dream has come true thanks to async/await. If you’re interested in understanding more about the internal mechanics and find out about corner cases, here’s an article you must read.

ANTS Performance Profiler Interested in how your async code performs? The Beta release of ANTS Performance Profiler 8 adds a new async profiling mode to help you understand where applications spend their time while doing async work. Try the Beta.


© Simple-Talk.com