Introduction.
The async
and await
keywords were introduced in Visual Studio 2012 with the aim of making the management of asynchronous methods more simple. This piece shows how best to use these keywords in order to produce efficient code that’s free of the many elusive bugs that inhabit the world of asynchronous programming.
Preventing the UI Thread from Blocking
A common situation is that you have a long-running method and you want the user controls to remain responsive while it is running and to be updated by the returned value of the method when it completes. The method is started by raising a button click event. This is what you can do.
private async void ReadWebPageEventHandlerAsync(object sender, RoutedEventArgs e)
{
string text ;
using (var client = new HttpClient())
{
text = await client.GetStringAsync(@"http://www.codeproject.com/");
}
this.textBox.Text = text;
}
The method's signature matches that of the event handler but is preceded by the async
modifier and, by convention, the method name ends in async
. The presence of the keyword async
does not mean that the method will run asynchronously. It means that the method has the ability to manage an asynchronous process. It is the await
keyword that initiates that process. Without the await
statement, the method will run entirely synchronously on the UI thread. Here’s a graphic.
It looks a bit like the solution to Hampton Court Maze but it’s supposed to show the progress of the UI thread through the async
method. The method is run in two stages. The code before the await
statement runs first on the UI thread. At the await
statement, the asynchronous process is started and the UI thread exits the method. When the asynchronous process ends, the UI thread returns, retrieves the payload and runs the rest of the method. It then goes merrily on its way. The value return from HttpClient.GetStringAsync
is of type Task<T>
, where T
, in this case, is a string
. The Task
entity represents the asynchronous method, it has various management functions and debugging aids. The important point is that, when it is awaited, it returns the payload T
. The method is truly asynchronous, a thread is used to start it and to retrieve the data when it finishes but for the rest of the time, there is no thread standing around, idle, twiddling its thumbs waiting for data to arrive from cyberspace.
A Little Bit about Threading
The async await
keywords provide enough abstraction for you not to have to work at thread level directly. There is certainly no need to martial the threads yourself, leave all that stuff to the async
method. You don’t want to be cooking food when you have your own chef. But it is useful to know a little about threading in order to code efficiently. There are two types of thread, foreground threads and background threads. Foreground threads are the main executing threads of the application, when they end the application ends. In UI applications, the foreground thread is the UI thread. Background threads can come and go as they please, they usually reside in the threadpool. The threadpool is a dynamic collection of readymade threads that are commonly used to do work that has been offloaded from the foreground threads. Running tasks on background threads when the processor is fully loaded and all cores are working does not increase efficiency. All that happens is that they are given a time-slice of the processor cycle in which they can run. So, in this situation, time is not saved, it's just split between tasks. This sort of arrangement is called parallel processing. Threads are expensive in terms of memory and their garbage collection is protracted, so it’s best to use them sparingly and to make sure that they are fully occupied when they are running. As you can see from the image below, the threadpool is fairly labour intensive.
Converting a Synchronous Method to Run Asynchronously
The first example used the HttpClient.GetStringAsync
method. This is a truly asynchronous method, most of the processing is done outside of the CPU on a server and no threads are used to run it. When you convert a synchronous method to an asynchronous one, it will run on the CPU from a threadpool thread. So, in a sense, it is a fake asynchronous method as it runs on a background thread. To convert, do this:
int result= await Task.Run(()=>MyLongRunningMethod());
If you wrap the Task.Run
call inside another async
method that simply calls Task.Run
, the extra await
statement will degrade performance. Also, calls to methods ending in Async
should be reserved for truly asynchronous methods. So doing this sort of thing is not recommended.
private async Task<int> MyLongRunningMethodAsync()
{
return await Task.Run(()=>MyLongRunningMethod());
}
Writing a Truly Asynchronous Method
To write an Asynchronous method that does not run on the threadpool, you need to be able to subscribe to an asynchronous event that fires when the method completes. The system.Timers.Timer
runs asynchronously and has such an event, so it’s possible to demonstrate the technique using this timer, but, in the real world, you would be interfacing with an external event.
public static Task<int> ProveRiemannsHypothesisAsync()
{
int result = 42;
var taskCompletionSource = new TaskCompletionSource<int>();
var timer = new Timer(1500);
timer.Elapsed += (sender, e) =>
{
timer.Stop();
timer.Dispose();
taskCompletionSource.SetResult(result);
};
timer.Start();
return taskCompletionSource.Task;
}
Dealing with Exceptions
With asynchronous methods, any exceptions raised are not thrown until the method’s result is returned at the await
statement. So the try
block needs to be around the await
. In the common situation where one async
method calls another async
method, the exception will ‘bubble up’ to the parent method.
static async Task<string> ReadFileAsync(string filename)
{
using (var reader = File.OpenText(filename))
{
return await reader.ReadToEndAsync();
}
}
public static async Task MainAsync()
{
string fileContents;
try
{
fileContents= await ReadFileAsync(@"C:\Temp\missingFile.txt");
}
catch (IOException e)
{
Console.WriteLine("Caught IOException: {0}", e.Message);
}
}
Async methods that return void
cannot be awaited and, if they raise an exception, they will take down the application. So it’s a good idea to reserve async void
methods for event handlers where you have no choice but to return void
. It’s ok to change a signature from async void
to async Task
, the compiler will handle the conversion. There is no need to new up a Task
. A Task
is best generated by calling an asynchronous method because what you get back is a hot Task
, a Task
that’s on active duty. The constructors generate cold Tasks
. Cold Tasks
are like cold soup – not easily consumed.
Cancelling Asynchronous Methods
Managing the cancellation of asynchronous method takes place by way of the CancellationTokenSource
class. Methods that support cancellation take the Token
returned from the CancellationTokenSource.Token
property as a parameter. Calling CancellationTokenSource.Cancel()
, cancels the method and calling CancellationTokenSource.CancelAfter(TimeSpan, timeSpan)
will timeout the method when the timeSpan
has passed. The cancellation should usually result in an OperationCanceledException
being thrown to indicate that the Task
was cancelled and did not just finish normally. Cancellation is not automatic, the cancellable method needs to check if the CancellationToken.IsCancellationRequested
property has been set to true
. The CancellationToken
is spent once the IsCancellationRequested
property is set to true
, so a new instance of CancellationTokenSource
is required to deal with the next cancellation request.
Reporting Progress
The Progress<T>
class is used to report progress, where T
is the type that indicates the progress made. It’s best to use a value or an immutable type because the reporting of progress is itself asynchronous. If you use a mutable reference, you will find that the async gremlins will have fun changing its value behind your back.
private readonly Progress<int> progressReporter;
private CancellationTokenSource cancellationTokenSource;
public MyViewModel()
{
progressReporter = new Progress<int>();
progressReporter.ProgressChanged +=
((sender, percent) => ProgressPercent = percent);
cancellationTokenSource = new CancellationTokenSource();
}
private int MyLongRunningMethod( CancellationToken ct,IProgress<int> progress=null)
{
int i = 0;
while (i < 100)
{
Thread.Sleep(50);
i++;
if (progress != null)
{
progress.Report(i);
}
ct.ThrowIfCancellationRequested();
}
return 42;
}
The SynchronizationContext and Console Applications.
The thread that the continuation part of an async
method runs on depends upon the SynchronizationContext
of the original thread that called the async
method. The SynchronizationContext
is a management entity that ensures that work posted to it is run in the correct context. With the button click example, the invoking thread will be the UI thread and that thread’s SynchronizationContext
runs work on the UI thread. What happens is that the SynchronizationContext
is captured at the await
statement. When the awaited method completes, the code after the await
statement, (the continuation), is converted into a delegate and posted to the captured SynchronizationContext
. That context then runs the delegate on the UI thread. With Console apps, the ‘Main
’ thread’s SynchronizationContext
runs work on the threadpool. So, even if the main thread is blocked, the continuation will be executed. This behaviour means that, to test methods designed to be run on a UI thread, you need to set the main thread’s SynchronizationContext
to one that mimics that of a UI thread and runs both parts of an async
method on the same thread. The downloadable example shows how to arrange this. The following code will run ok without a UI thread but will block with one.
private static void Main(string[] args)
{
MainAsync(args);
Console.WriteLine("Waiting for MainAsync to finish.");
Console.ReadLine();
}
public static async Task MainAsync(string[] args)
{
await Task.Delay(TimeSpan.FromSeconds(3));
Console.WriteLine("MainAsync has finished\nPress return to end");
}
Using the ConfigureAwait Extension Method
It is possible to change the default behaviour of an async
method so that the continuation part of the method always runs on the threadpool instead of the SynchronizationContext
of the thread that invokes the method. All you need to do is add the extension ConfigureAwait(false)
to the awaited method. You can improve performance by employing this technique but it’s not a good idea to use it when updating UI controls. They need to be set on the thread that created them.
public static async Task AsyncMethod()
{
Needle needle = await LookForNeedleInHaystackAsync().ConfigureAwait(false);
Console.WriteLine("Found a size {0} needle", needle.Size);
}
Unit Testing Asynchronous Methods
The basic technique for unit testing asynchronous methods is to write a test method with a signature that returns a Task
and is prefaced by the async
modifier. There should always be an await
statement in the body of the test method that invokes the particular async
method under test. Something like this:
[TestMethod]
public async Task LookForNeedleInHaystackAsyncReturnsSize10Needle()
{
Needle needle = await Program.LookForNeedleInHaystackAsync();
Assert.AreEqual(needle.Size, 10);
}
Exceptions can be tested by using the ExpectedException
attribute and simply awaiting the method under test.
[TestMethod]
[ExpectedException(typeof(ArgumentException))]
public async Task LongRunningAlgorithmAsyncThrowsArgumentExceptionWhenSeedIsZero()
{
await Program.LongRunningAlgorithmAsync(0);
}
It’s not a good idea to allow the method under test to call non CPU bound code in its await
statement. These sorts of calls depend, to a greater or lesser extent, upon unpredictable external devices that can take an inordinate amount of time to complete. The trick here is to replace the call with one that invokes a substitute method that runs entirely on the CPU. There are two main approaches to doing this. The first is to construct the substitute so that it returns a completed Task
using Task<T>.FromResult(T instance)
. This will run synchronously.
var SubstituteForDataAcccessLayer = Substitute.For<IDataAcccessLayer>();
SubstituteForDataAcccessLayer.GetStockLevelAsync(0).ReturnsForAnyArgs(Task.FromResult(4));
The second approach is to have the mock object's method behave more like an asynchronous method by getting it to post a continuation to the captured SynchronizationContext
's dispatcher queue . This can be quite useful as some elusive bugs can be caused by continuations running at a point in the application that you didn’t anticipate.
SubstituteForDataAcccessLayer.GetStockLevelAsync(0).ReturnsForAnyArgs(
async x =>
{
await Task.Yield();
return 4;
});
Debugging Async Methods
It can be useful, when debugging, to employ a method that reports the nature of the current thread and the state of the threadpool at various points in the application. Something like this:
public static void ShowThreadInfo(string origin)
{
int workerThreads;
int completionPortThreads;
ThreadPool.GetAvailableThreads(out workerThreads, out completionPortThreads);
Console.WriteLine(
"Location: {0}\n Current thread is {1}.
Id {2}\nThreadpool threads available {3}\n",
origin,
Thread.CurrentThread.IsThreadPoolThread ? "Threadpool" : "Foreground",
Thread.CurrentThread.ManagedThreadId,
workerThreads);
}
You can then display this sort of information in a Console window.
It would appear, from the display, that StreamReader.ReadToEndAsync()
is utilizing a threadpool thread.
Sending Concurrent Requests to a Server
This technique sends multiple requests to the server, asynchronously. Then it uses an await
statement that doesn’t return until all the data has been up loaded. Here’s an example using Pizza
s that are accessed by their order number.
public static async Task<List<Pizza>> LoadPizzasAsync(IEnumerable<int> orderNumbers)
{
var queue = new Queue<int>(orderNumbers);
var tasks = new List<Task<Pizza>>();
while (queue.Count > 0)
{
int orderNumber = queue.Dequeue();
Task<Pizza> task = LoadPizzaFromServerAsync(orderNumber);
tasks.Add(task);
}
Pizza[] loadedPizzas = await Task.WhenAll(tasks);
return loadedPizzas.ToList();
}
As this runs asynchronously, the chances are that the returned Pizza
s will not be in the same sequence as they were when ordered. To avoid disappointment, it’s best to sort them before dishing them out.
Throttling Concurrent Requests to a Server.
In the previous example, the server could become overloaded with concurrent requests as all the requests were sent at once. In this example, the number of requests is limited (throttled) to the maximum concurrency level of the server. The following code is based on this excellent presentation by Mads Torgersen.
public async Task<List<Pizza>>
LoadPizzasAsync(IEnumerable<int> orderNumbers, int maxConcurrency)
{
int throttlingLevel = maxConcurrency;
var tasks = new List<Task>();
var pizzaList = new List<Pizza>();
var queue = new Queue<int>(orderNumbers);
int concurrentCalls = 0;
while (concurrentCalls < throttlingLevel && concurrentCalls < queue.Count)
{
tasks.Add(this.GetPizzaAsync(queue, pizzaList));
concurrentCalls++;
}
await Task.WhenAll(tasks);
return pizzaList;
}
private async Task GetPizzaAsync(Queue<int> queue, List<Pizza> pizzaList)
{
while (queue.Count > 0)
{
int orderNumber = queue.Dequeue();
Pizza pizza = await this.LoadPizzaFromDatabaseAsync(orderNumber);
pizzaList.Add(pizza);
}
}
Conclusion
Harnessing the full power of multi-cored processors depends upon keeping the cores fully occupied. It’s hoped that the information given here will help you put them through their paces without falling into the many elephant traps that await
the asynchronous coder. Sorry about the pun, I tried to resist it but couldn't.
References
History
- 20th May, 2016: Initial version