Once in a while, the technical presentations that I conduct at my day job force me to learn something new, something I'd never had to deal with before.

This happened just last week, when my coworkers voted for the topic of the next session to be Async and Await in .NET. These two keywords make writing asynchronous code much simpler than it was before .NET 4.5.

I'll be perfectly honest: until last week I know nothing about asynchronous programming. Nada. Zip. Zilch. But after doing some research and creating my own sample project, I'm starting to see why Stephen Cleary said that "Async will fundamentally change the way most code is written".

In the spirit of not caring that I suck as long as I'm learning I decided to just dive right into learning about async, await, and asynchronous programming in general. I had one problem though: I found precious few resources that were able to explain to me the concepts of asynchronous programming in simple terms.

I seek to remedy that with this post.

I'm not going to go into great detail about the ins and outs of asynchronous programming in .NET; rather, I'm be focusing on taking these opaque concepts and breaking them down into simple, understandable examples. This helped me better understand exactly what I'm doing when I use these keywords, and hopefully it'll help you too. Let's get started!

The Sample Project

I built a sample project using Visual Studio 2015 and ASP.NET MVC (including the ContentManagement class and HomeController we used earlier), and it's on GitHub, so go check it out!

exceptionnotfound/AsyncAwaitDemo
Contribute to exceptionnotfound/AsyncAwaitDemo development by creating an account on GitHub.

What is Asynchronous Programming?

Asynchronous programming is writing code that allows several things to happen at the same time without "blocking", or waiting for other things to complete. This is different from synchronous programming, in which everything happens in the order it is written (if you write code for a living, chances are it will be synchronous code).

Let's look at a synchronous C# method:

public string GetNameAndContent()
{
    var name = GetLongRunningName(); //Calls another webservice, can take up to 1 minute.
    var content = GetContent();  //Takes up to 30 seconds
    return name + ": " + content;
}

Every time something calls this method, the caller has to wait up to 1 minute before it can resume processing. That's a minute of wasted time, time it could be spending doing other tasks.

With .NET asynchronous programming, we can modify this method like so:

public async Task<string> GetNameAndContent()
{
    var nameTask = GetLongRunningName(); //This method is asynchronous
    var content = GetContent(); //This method is synchronous
    var name = await nameTask;
    return name + ": " + content;
}

We made three changes to the method:

  1. We marked the method as async. This tells the compiler that this method can run asynchronously.
  2. We used the await keyword on the nameTask variable. This tells the compiler that we will ultimately need the result of the GetLongRunningName() method, but we don't need to block on that call.
  3. We changed the return type to Task<string>. This informs the caller method that the return type will eventually be string, but not right away and that can do other things while GetLongRunningName() is processing.

But even this wasn't obvious to me. What were we actually doing when "waiting" for GetLongRunningName() to finish?

This is the difficult part to put in simple terms, but I'll try anyway. Essentially, the system wants to execute GetLongRunningName() because it was called first, but because it is an async task and we are awaiting it, control is relinquished to fire GetContent(), which means we now have the work for two methods running at the same time. What this does not do is spin up another thread; using async and await do not cause threads to be created.

(If you want a more in-depth explanation of what happens during an async invocation, MSDN has you covered.  It's the best example I could find, complete with a diagram.)

Waiting on Multiple Calls

Let's see another example. Say I have a class called ContentManagement, which has both synchronous and asynchronous methods:

public class ContentManagement
{
    public string GetContent()
    {
        Thread.Sleep(2000);
        return "content";
    }

    public int GetCount()
    {
        Thread.Sleep(5000);
        return 4;
    }

    public string GetName()
    {
        Thread.Sleep(3000);
        return "Matthew";
    }
    public async Task<string> GetContentAsync()
    {
        await Task.Delay(2000);
        return "content";
    }

    public async Task<int> GetCountAsync()
    {
        await Task.Delay(5000);
        return 4;
    }

    public async Task<string> GetNameAsync()
    {
        await Task.Delay(3000);
        return "Matthew";
    }
}

ContentManagement is just a simple demo class that imitates having some long running calls. Note that three of the methods are marked async, and (by convention) the word Async is appended to the method name. We'll explain why we need both synchronous and asynchronous methods in the Potential Issues section below.

Now let's write an MVC controller, like so:

public class HomeController : Controller
{
    [HttpGet]
    public ActionResult Index()
    {
        Stopwatch watch = new Stopwatch();
        watch.Start();
        ContentManagement service = new ContentManagement();
        var content = service.GetContent();
        var count = service.GetCount();
        var name = service.GetName();

        watch.Stop();
        ViewBag.WatchMilliseconds = watch.ElapsedMilliseconds;
        return View();
    }

    [HttpGet]
    public async Task<ActionResult> IndexAsync()
    {
        Stopwatch watch = new Stopwatch();
        watch.Start();
        ContentManagement service = new ContentManagement();
        var contentTask = service.GetContentAsync();
        var countTask = service.GetCountAsync();
        var nameTask = service.GetNameAsync();

        var content = await contentTask;
        var count = await countTask;
        var name = await nameTask;
        watch.Stop();
        ViewBag.WatchMilliseconds = watch.ElapsedMilliseconds;
        return View("Index");
    }
}

(As an aside, the Stopwatch class is super useful when trying to record time of execution.)

Notice what these two actions are doing. In both cases, the actions are calling the methods in the ContentManagement service, but in one they are doing it asynchronously.

On our Index view, we have an output which displays the WatchMilliseconds value. Let's see what the Index action renders:

A snippet of the Index view, showing that 10002 milliseconds elapsed.

Intuitively, this makes sense. We called three methods; they took 2 seconds, 5 seconds, and 3 seconds respectively to execute; so the total execution time should be around 2 + 5 + 3 = 10 seconds.

Now watch what happens if we call the IndexAsync action:

A snippet of the IndexAsync action's view, showing that the method took 5007 milliseconds to execute.

Where did we get that number? The longest-running of the three tasks takes 5 seconds. By designing this to use async, we cut our total execution time in half! That's a lot of speedup to be gained from writing a little extra code!

Return Types

OK, great, so why we'd want to do this makes sense now, at least to me.  But what exactly is that Task<ActionResult> thing we'd used in the IndexAsync action?

There are three return types we can use when writing an async method:

  • Task: This class represents an asynchronous operation, and can be awaited.
  • Task<T>: This class represents an asynchronous operation that returns a value, and can be awaited.
  • void: If an async method returns void, it cannot be awaited. This essentially turns the method into a "fire and forget" method, which is rarely the desired behavior. Further, the error handling on async methods that return void is quite a bit different, as shown by Stephen Cleary. There's no good reason to use void as a return type from an async call unless you do not care at all whether that call actually finishes.

In short, almost every asynchronous method will use Task or Task<T> as their return type. The Task class represents the asynchronous action itself, not the result of that action. Calling await on the Task means that we want to wait for the Task to complete, and in the case of Task<T>, want to retrieve the value that the Task returns.

Potential Issues

Let's be clear about something: most apps will probably not see such dramatic improvements (50% speedup!) from implementing asynchronous programming. Again, we're not going to be turbo-charging individual methods, just executing them at the same time. In fact, if we design our asynchronous methods incorrectly, we can actually harm overall performance.

When we mark a method as async, the compiler generates a state machine in the background; this is extra code. If we write good, stable asynchronous code, the amount of time it takes to create this extra structure won't impact us at all, since the benefits of running asynchronously outweigh the cost of building the state machine. However, if our async method doesn't have an await, the method will run synchronously, and we will have spent extra time creating the state machine that we didn't use.

There's one other potential problem to be aware of. We cannot call an asynchronous method from a synchronous one. Because async and await should go together in all cases, we pretty much have to have async on all methods, all the way down. This is why we needed separate async methods in the ContentManagement class earlier. Ultimately, this results in having more code, which means more things that can theoretically break. However, given a good design and solid understanding of what we are trying to accomplish, having that extra code also leads to greater performance, so in my mind it is a fair trade-off.

Summary

A little extra effort in implementing asynchronous goes a long way to improving performance and responsiveness of our apps. .NET has made it easy to do with the async and await keywords, and now we can implement asynchronous design simply, concisely, and quickly.

I went into this exercise knowing precisely nothing about asynchronous programming; now I can at least say I know more than nothing. Hopefully, you can too (and if you can, let me know in the comments!)

If you need more information, take a look at Async in C# 5.0 by Alex Davies, which I've read and helped immensely when trying to understand what async/await does in an MVC web application.

Happy Coding!