Skip to main content

C# - Asynchronous programming using async and await [Intermediate]


You're using your favorite desktop application when suddenly it freezes. You can't interact with it and the UI won't refresh, until all of a sudden everything goes back to normal. Sounds familiar? I'm sure this has happened to you before. What most likely happens in such cases is that the application performs a lengthy operation using the UI thread, which in turn isn't available to take care of the UI until the operation ends.

To provide a responsive UI experience, potentially lengthy operations should be done asynchronously, on a separate thread. This isn't anything new, but many applications still perform operations on the UI thread. Why? One of the reasons is that asynchronous programming can be a pain. It is far more complex, harder to write, test and maintain and if not done properly can lead to degraded performance and even deadlocks. Over the years, versions of .NET offered incrementally improved models that attempted to reduce the complexity of asynchronous programming. But it wasn't until .NET 4.5 / C# 5 that it finally happened: now we can write asynchronous code almost as easily as we write synchronous code and the compiler does all the heavy lifting for us. In fact, asynchronous code can now be almost identical to its synchronous counterpart. Without further ado, let's examine async and await.

 

Introducing async and await

Imagine that we have a Windows Forms application. When the user clicks a button, we want to download an image from the web and display it in the UI. The following code does that synchronously.
private void button_Click(object sender, EventArgs e)
{
    WebClient client = new WebClient();
    byte[] imageData = client.DownloadData("imageurl");
    this.pictureBox.Image = Image.FromStream(new MemoryStream(imageData));
}
 
Now imagine that the internet is slow and it takes 30 seconds to download the image. During this time the application will become unresponsive. If the user is impatient (and rightfully so), she may terminate the application before it even has a chance to download the image. Clearly this isn't a great approach. We must download the image asynchronously. Let's see how we can easily do that using async and await.
private async void button_Click(object sender, EventArgs e)
{
    WebClient client = new WebClient();
    byte[] imageData = await client.DownloadDataTaskAsync("http://imageurl");
    this.pictureBox.Image = Image.FromStream(new MemoryStream(imageData));
}
 
This code is almost identical! This is how easy it is to write asynchronous code using async and await. There are three differences in this revised code: the method is decorated with the async keyword, the call to download the image is preceded by await, and the method that is called to perform the actual download is DownloadDataTaskAsync, which is the asynchronous counterpart of the DownloadData method. Let's discuss each of these changes in more detail.

The async keyword decorates the method and tells the compiler that this method may contain the await keyword. This is necessary for backward compatibility with code that was written prior to the introduction of the await keyword in C# 5 and may use await as an identifier. It also allows the compiler to make sure that we use await inside the method, and emit a warning if we forget to do so.
The DownloadData method of the WebClient class downloads the data synchronously before returning the control to the caller. But we need a method that returns immediately and performs the actual download asynchronously. Many of the .NET FCL classes now offer asynchronous versions of their methods where applicable. As a convention, asynchronous methods that can be awaited are named in the form of xxxAsync. In the case of the WebClient class however, there was already a DownloadDataAsync method prior to .NET 4.5, so the DownloadDataTaskAsync method was added. When you're in doubt which method to use with 'await', here is the ultimate guideline: only methods that return Task or Task<T> can be awaited.

The await keyword is probably the most interesting change we introduced to the code. Once we start the download operation asynchronously by calling the appropriate method, we want to release the UI thread until the download is complete. The await keyword does exactly that. Whenever our code encounters an await, the method actually returns. Once the awaited operation completes, the method resumes. That is, it continues executing from where it stopped.

 

Using await

In the example above we used await only once, but we can use it multiple times in the same method. For example, we could use it to download multiple images. This means that the method would return and resume multiple times.

Another interesting aspect of await is that we can use it almost anywhere in the code, not just as part of a simple method call. Consider this revised button_click handler.
private async void button_Click(object sender, EventArgs e)
{
    WebClient client = new WebClient();
    this.pictureBox.Image = Image.FromStream(
        new MemoryStream(await client.DownloadDataTaskAsync("http://imageurl")));
}

 

Rules and Limitations

While usage of async and await is very flexible, it has some limitations. Here are the most notable ones:
  • The await keyword may not be used inside a catch or finally block (remember that await actually causes the method to return and later resume, and it's not possible to resume back into a catch / finally)
  • Constructors and property accessors cannot be marked as async and use await
  • An async method cannot have a ref or out parameters
  • The await keyword may not be used in the body of a lock statement

 

Exception Handling

Asynchronous programming in C# 5 was designed to be as seamless as possible, and exception handling isn't an exception (pun intended). In fact, we write exception handling code the same way we do in synchronous programming, as shown in the following code example:
private async void button_Click(object sender, EventArgs e)
{
    WebClient client = new WebClient();
    try
    {
        this.pictureBox.Image = Image.FromStream(
            new MemoryStream(await client.DownloadDataTaskAsync("http://someurl")));
    }
    catch (WebException exp)
    {
        // show error message
    }
}
 
Even though the work to download the image happens on another thread, and consequently the exception may be thrown in that other thread, it is captured and rethrown when the control returns to the awaiting method. This allows our code to handle the exception as if we made a synchronous call. One interesting question is what happens with finally blocks. Consider the following code:
WebClient client = new WebClient();
try
{
    this.pictureBox.Image = Image.FromStream(
        new MemoryStream(await client.DownloadDataTaskAsync("http://someurl")));
}
finally
{
    // do something
}
finally blocks are guaranteed to execute when the control leaves the corresponding try and catch blocks. As a result it is also guaranteed to execute if the method returns from within the try or catch blocks. As we already know, the await keyword makes the method return. So what will happen in the code above? Will the finally code execute when returning due to the await or will it execute only after the method resumes and the code logically leaves the try block?

Whenever you ask yourself such a question, it's useful to keep in mind that the asynchronous support in C# 5 was designed to feel as natural (and synchronous-like) as possible. What happens in asynchronous code is generally what would happen in a corresponding synchronous code. If the code above was synchronous, you would expect the finally block to execute after the code in the try block completes in its entirety (assuming that no exception is thrown). The same applies when the try block contains asynchronous code. Even though technically the method returns as a result of the await keyword, logically it hasn't returned yet. Hence the finally block in the code above will execute when the code in the try block completes (or if an exception is thrown), as it would in synchronous code.

 

How Does it Work?

C# 5 allows us to write asynchronous code using simplified syntax, and leave the dirty work to the compiler. Behind the scenes when an asynchronous method is compiled, the compiler generates a state machine. Without getting into the nitty gritty detail, here is a simplified description of how it works. 

The state machine has a method that contains the code of our original method, but broken into multiple segments and embedded in a switch block. When the method executes, it inspects the state and runs the appropriate code segment. When the method executes for the first time, it starts with the first state, hence it starts executing from the beginning of our original method. Recall that we can only await methods that return Task or Task<T> (which derives from Task). When we await a method, the Task object is used to register a callback that will be invoked when the asynchronous operation completes. When that happens, the method of the state machine is executed again. But this time the state is different, so the method executes the next segment of our original code, which is the part that comes after the await.

The is a simplified description how this works. There are more details, but that's the main idea.

 

Summary

C# 5 and .NET 4.5 take a huge step forward in making asynchronous programming simple and almost seamless. With the introduction of new C# keywords, as well as the introduction of many asynchronous versions of methods of FCL types, asynchronous code now appears to be almost identical to its synchronous counterpart. This also makes it relatively easy to migrate synchronous code into asynchronous code that promotes responsive applications and happy users...

In an upcoming tutorial I will cover some more advanced asynchronous programming features.

Comments

Popular posts from this blog

C# Snippet - Shuffling a Dictionary [Beginner]

Randomizing something can be a daunting task, especially with all the algorithms out there. However, sometimes you just need to shuffle things up, in a simple, yet effective manner. Today we are going to take a quick look at an easy and simple way to randomize a dictionary, which is most likely something that you may be using in a complex application. The tricky thing about ordering dictionaries is that...well they are not ordered to begin with. Typically they are a chaotic collection of key/value pairs. There is no first element or last element, just elements. This is why it is a little tricky to randomize them. Before we get started, we need to build a quick dictionary. For this tutorial, we will be doing an extremely simple string/int dictionary, but rest assured the steps we take can be used for any kind of dictionary you can come up with, no matter what object types you use. Dictionary < String , int > origin = new Dictionary < string , int >();

C# Snippet - The Many Uses Of The Using Keyword [Beginner]

What is the first thing that pops into your mind when you think of the using keyword for C#? Probably those lines that always appear at the top of C# code files - the lines that import types from other namespaces into your code. But while that is the most common use of the using keyword, it is not the only one. Today we are going to take a look at the different uses of the using keyword and what they are useful for. The Using Directive There are two main categories of use for the using keyword - as a "Using Directive" and as a "Using Statement". The lines at the top of a C# file are directives, but that is not the only place they can go. They can also go inside of a namespace block, but they have to be before any other elements declared in the namespace (i.e., you can't add a using statement after a class declaration). Namespace Importing This is by far the most common use of the keyword - it is rare that you see a C# file that does not h

C# WPF Printing Part 2 - Pagination [Intermediate]

About two weeks ago, we had a tutorial here at SOTC on the basics of printing in WPF . It covered the standard stuff, like popping the print dialog, and what you needed to do to print visuals (both created in XAML and on the fly). But really, that's barely scratching the surface - any decent printing system in pretty much any application needs to be able to do a lot more than that. So today, we are going to take one more baby step forward into the world of printing - we are going to take a look at pagination. The main class that we will need to do pagination is the DocumentPaginator . I mentioned this class very briefly in the previous tutorial, but only in the context of the printing methods on PrintDialog , PrintVisual (which we focused on last time) and PrintDocument (which we will be focusing on today). This PrintDocument function takes a DocumentPaginator to print - and this is why we need to create one. Unfortunately, making a DocumentPaginator is not as easy as