left-icon

C# Succinctly®
by Joe Mayo

Previous
Chapter

of
A
A
A

CHAPTER 8

Making Your Code Asynchronous

Making Your Code Asynchronous


In version 5, C# introduced the capability to write and call code asynchronously, commonly referred to as async. To understand async, it’s useful to consider the normal behavior of code, which is synchronous. In synchronous code you call a method, wait for it to complete, and move on to the rest of the code. The primary point of this behavior is that the thread calling the synchronous method is also executing the synchronous code in that method. If that synchronous method runs for a long time, your UI will become unresponsive and your users might not know whether the program crashed or if they should just wait.

Asynchronous code improves this situation by allowing the long-running operation to continue on a separate thread, and free your calling thread to resume its responsibilities. When the calling thread is the UI thread, the application becomes responsive again and you can show a status or busy indicators, or let the user operate another part of the program while the asynchronous process runs. When the asynchronous process returns, you can interact with users in some way, if that makes sense for your application. In the past, writing this asynchronous code has been a challenge. Though the task of writing asynchronous code has improved with new patterns and libraries, the C# async features makes asynchronous programming much easier.

There are a couple different perspectives of async that determine how you code: library consumer or library creator. From a consumer perspective, you make assumptions about async code based on an implied contract. However, from the library creator perspective, you have additional responsibilities to ensure your code provides the async contract users expect.

Consuming Async Code

C# has two keywords that support async: async and await. Decorating a method with the async modifier says that the method can contain async code. You use the await keyword on a Task to start an async operation.

using System.IO;

using System.Threading.Tasks;

public class Program

{

    public static void Main()

    {

        Program.CreateFileAsync("test.txt").Wait();

    }

    public static async Task CreateFileAsync(string filename)

    {

        using (StreamWriter writer = File.CreateText(filename))

            await writer.WriteAsync("This is a test.");

    }

}

In the previous program, the CreateFileAsync method is asynchronous. You can tell by the async modifier on the method. You need to add using clauses for the System.IO and System.Threading.Tasks namespaces for writing to a file and async Task support, respectively. The File class is part of the FCL and its CreateText method returns a StreamWriter that you use to write to the file.

Note: Appending a method name with Async is not required, but it is a common convention.

The proper way to call an async method is to await its Task or Task<T>. The WriteAsync method returns Task, which means you can await it.

The using statement closes the file when its enclosing block completes. In this case, the block is only a single line, so no curly braces are required.

Part of the async contract is an expectation that some code in the library you’re using will run the operation on another thread, releasing your thread for other operations; that’s what WriteAsync does too. So, the thread returns to the code calling this async method. But the caller in this program is the Main method, which is calling Wait() on the Task returned from CreateFileAsync. This keeps the program from ending before the thread that’s running the async operation completes.

Warning: The previous example is a console application, which doesn’t have the underlying infrastructure (referred to as a synchronization context) to manage proper thread handling. Therefore, it was necessary to Wait()  on the task returned from CreateFileAsync. In a normal UI application, you will have a synchronization context, meaning you won’t have to worry about the program ending, and won’t need to call Wait()  on an async method. The preferred method of waiting on an async method is via async and await as shown in the CreateFileAsync method. In fact, you should never call Wait on an async method. That’s because when the second thread returns from doing work on the async call, it will attempt to marshal the call back onto the calling thread. If that calling thread is in a synchronous Wait(), the thread will be blocked, preventing the second thread from performing that marshaling operation. Then you’ll have a deadlock. To prevent deadlock, never call Wait(), use async and await instead.

The async modifier is required on the method if you use await. If a method has the async modifier, but no awaits, C# will give you a compiler warning and let you know that the method will run synchronously.

Async Return Types

With async, you can await any awaitable type. The FCL has Task and Task<T>, which are awaitable and are what you should use in most situations. Returning Task means that the method does not return a value, which is what you saw with the previous CreateFileAsync method.

Tip: Stephen Toub’s blog post “await anything;” explains how to create a custom awaitable type and is a good reference if you see it as a way to improve your code. You can read it at http://blogs.msdn.com/b/pfxteam/archive/2011/01/13/10115642.aspx.

Use Task<T> when your method returns a value. The following listing shows an example.

    public async Task<string> ReturnGreeting()

    {

        await Task.Delay(1000);

        return "Hello";

    }

Task.Delay is a way to sleep the thread by a number of milliseconds, but I’ll be using it in more examples to simplify code and as a placeholder for where you would normally add async code.

The previous example shows a return type of Task<string>. The method only returns the string "Hello" instead of an instance of Task<string> because the C# compiler takes care of that for you.

An async method can return void rather than an awaitable type too. This is done in the following listing.

    public async void SayGreeting()

    {

        await Task.Delay(1000);

        Console.WriteLine("Hello");

    }

This method executes asynchronously, but async void methods have important caveats you must be aware of: they aren’t awaitable, and they don’t protect against exceptions, but they are necessary for scenarios like event handling where the method must be void.

Since you can only await awaitable types like Task and Task<T>, there’s no way to await an async void method. The implication of this is that when a library’s code starts another thread, it allows the calling thread to return. Calling an async void method means you can’t wait until that method completes and you won’t ever know when or if the method completes. As with anything, there are no absolutes and one could argue that it would be possible to write some cross-thread communication mechanism, but I’m referring to the general out of the box behavior, which will lead to some important implications. Because of this behavior, there are pros and cons on when you should use an async void method.

The largest problem with async void methods is that you can’t throw an exception back to the calling code. With Task and Task<T> returning methods, you can await and wrap the async method call in a try-catch, but you can’t do that with async void methods. If an async void method throws an unhandled exception, the application will crash.

With such problems, it would be easy to assume that async void should not be used at all. However, the C# language designers added async void for one specific reason: to support event handling. Event handlers in the .NET Framework follow a pattern where their delegates return void. Therefore, you can’t use an awaitable type, like Task or Task<T>, and must assign async void methods as event handlers.

In UI applications, a UI control might fire an event, async void methods assigned to the event execute, the async code starts a new thread and releases the UI thread, and the UI thread returns and processes messages to keep the UI responsive. So, using async void as event handlers is appropriate.

Developing Async Libraries

Writing an async library is mostly normal coding, but the key thing to keep in mind is what is happening to the thread. First, all code executes by default on the calling thread. Second, you need to marshal execution onto a new thread and release the calling thread to the caller.

Understanding What Thread the Code is Running On

The following code doesn’t necessarily make any logical sense, but represents the potential structure of some library code that you might write. In particular, it demonstrates what happens with threads before and after the first await in your async method. In the following code, UserInfo is just a type to hold and return data. UserService and AddressService have async methods that the GetUserInfoAsync method calls.

using System.Collections.Generic;

using System.Linq;

using System.Threading.Tasks;

public class UserInfo

{

    public string Info { get; set; }

    public string Address { get; set; }

}

class UserService

{

    internal static async Task<string> GetUserAsync(string user)

    {

        // Do some long running synchronous processing.

        return await Task.FromResult(user);

    }

}

class AddressService

{

    internal static async Task<string> GetAddressAsync(string user)

    {

        return await Task.FromResult(user);

    }

}

public class UserSearch

{

    public async Task<UserInfo> GetUserInfoAsync(string term, List<string> names)

    {

        var userName =

            (from name in names

             where name.StartsWith(term)

             select name)

            .FirstOrDefault();

        var user = new UserInfo();

        user.Info = await UserService.GetUserAsync(userName);

        user.Address = await AddressService.GetAddressAsync(userName);

        return user;

    }

}

Remember, you’re writing reusable library code, so it could be called from many different technologies, such as WPF, Windows Store apps, Windows Phone, and more. What’s common about these type of applications is that a user interacts with the UI controls and those UI controls fire events. This means an async void event handler awaits your GetUserInfoAsync method.

When the event handler code calls your code, it’s running on the UI thread. Your code will continue running on the UI thread until some other code explicitly marshals the call to another thread and releases the UI thread.

Note: More accurately, the thread calling your code might not necessarily be the UI thread if there was another async method that called your code and already released the UI thread. However, defensive coding is a safe approach because there exists the possibility that your code will be called on the UI thread by some developer in the future.

Notice the LINQ query in GetUserInfoAsync before reaching the first await. That is synchronous code that runs on the calling thread, which could also be the UI thread. The issue here is that the UI thread is tied up doing work in your async method, rather than returning to the UI. Imagine a UI with a progress indicator that locks up because your async method is holding onto the UI thread and doing a lot of processing before the first async call.

The code is still on the UI thread when it calls UserService.GetUserAsync. I added a comment to GetUserAsync to represent more long-running synchronous processing that is also running on the UI thread. Finally, awaiting Task.FromResult releases the UI thread and the rest of the code runs asynchronously. That’s because Task.FromResult implements the async contract properly. Before showing you how to fix this problem, let’s look at the rest of the code so you can understand how it runs.

When the code returns from Task.FromResult, the UI thread has been released and the code is running on the new async thread. When returning from GetUserAsync to its caller, GetUserInfoAsync, the call automatically marshals back to the calling thread, which could be the UI thread. Again, this program eats up CPU cycles on the UI thread, making the application less responsive. Fortunately, there’s a way to fix this problem.

Fulfilling the Async Contract

The previous section explained how the code runs on the calling thread by default, which could be the UI thread. Whenever you call an async method in the FCL, that code will release the calling thread and continue on a new thread, which is proper behavior of the async contract that developers expect. You should do the same in your code.

To do this, use the Task.ConfigureAwait method, passing false as the parameter. The following is an example that fixes the problem in GetUserInfoAsync.

    public async Task<UserInfo> GetUserInfoAsync(string term, List<string> names)

    {

        var userName =

            (from name in names

             where name.StartsWith(term)

             select name)

            .FirstOrDefault();

        var user = new UserInfo();

        user.Info = await UserService.GetUserAsync(userName).ConfigureAwait(false);

        user.Address = await AddressService.GetAddressAsync(userName);

        return user;

    }

The GetUserInfoAsync method appends ConfigureAwait(false) to the call to GetUserAsync. GetUserAsync returns a Task<string> and ConfigureAwait(false) operates on that return value, releasing the calling thread and running the rest of the method on a new async thread. That’s it; that’s all you have to do.

You still have the issue of synchronous processing before the first call to ConfigureAwait. Sometimes, you can’t do anything about it because it’s necessary to execute that code before the first await. However, if it’s possible to rearrange the code to do any processing after the first await, you should do so.

A Few More Notes on Async

I made this point previously, but I feel it bears repeating. Especially for library code, you should prefer async methods that return Task or Task<T>. In the UI you don’t have a choice if you’re writing an event handler. If you’re using a Model View ViewModel (MVVM) architecture, you’ll also need void Command handlers. You shouldn’t have these issues in reusable library code and in this scenario, async void methods are dangerous.

Async void methods that throw exceptions will crash your application.

Much of the discussion in this chapter is around how async improves the user experience by releasing the UI thread. In addition to that, async also improves application performance by not blocking threads. These scenarios usually involve some type of out-of-process operation such as network communication, file I/O, or REST service calls. These operations can use Windows operating system services such as I/O completion ports to free threads while the long-running out-of-process operation executes; they can then reallocate those threads when the operation completes and needs to return to your code. In addition to performance increases through efficient thread management, you can also improve the scalability of a server application by using async to avoid blocking threads more than you have to.

For all the seeming complexity that this chapter introduces, attempting to perform many of the operations associated with managing threads for application responsiveness, performance, and scalability is made much easier through the use of async.

Summary

Async is a useful capability that allows your application to be responsive and perform well. The user experience of async is a method with an async modifier and the ability to await an async method’s Task. In addition to the user experience, there are additional considerations for writing async libraries. You should be aware of the threading behavior and how an async method runs on the caller’s thread by default. Remember that you should minimize synchronous code before the first await and that you should call ConfigureAwait(false) at the earliest opportunity, releasing the UI thread and running the remaining algorithm on the new async thread.

Scroll To Top
Disclaimer
DISCLAIMER: Web reader is currently in beta. Please report any issues through our support system. PDF and Kindle format files are also available for download.

Previous

Next



You are one step away from downloading ebooks from the Succinctly® series premier collection!
A confirmation has been sent to your email address. Please check and confirm your email subscription to complete the download.