Async and await and the UI thread

A reader posted a question on my blog about the use of ConfigureAwait. His question was about the following paragraph I wrote for the book Programming in C#:

“Both awaits use the ConfigureAwait(false) method because if the first method is already finished before the awaiter checks, the code still runs on the UI thread”

What is this all about? Well, imagine the following scenario. You are working on a WPF or WinForms application and you have some asynchronous code. The beauty of async and await is that it automatically keeps track of the synchronization context you are on and makes sure that your code will run on the correct thread.

Take the following code:

1:  private async void button1_Click(object sender, EventArgs e)
2:  {
3:      HttpClient client = new HttpClient();
4:      string result = await client
                        .GetStringAsync("http://microsoft.com");
5:      label1.Text = result;
6:  }

(You can run tis code by creating a new WinForms application with a button and a label. If you then wire up the click event to the previous code, you can run the example).

This code executes an asynchronous request to fetch microsoft.com as a string and then displays the resulting HTML in the label.
In applications like WinForms and WPF, there is only one single thread that has access to the elements on screen. Async and await makes sure that the reminder of your method will run on the UI thread so you can access the label. It does so by capturing the current SynchronizationContext and restoring it when the async method is finished.
But what if you don’t want to run the reminder of your method on the UI thread? Maybe you have another asynchronous operation, like writing the content to a file, that doesn’t have to run on the UI thread and the capturing of the SynchronizationContext and the thread switching will only cost you performance.
You can turn of this behavior by using ConfigureAwait(false):
await SomeFastAsyncOperation().ConfigureAwait(false);

By using ConfigureAwait(false) you disable the capturing of the SynchronizationContext and you let the .NET Framework know that you can continue on any thread that’s available. But now take the following code where you have multiple asynchronous operations:

   
1:  private async void button1_Click(object sender, EventArgs e)
2:  {
3:      Console.WriteLine("Current thread: " +    
                           Thread.CurrentThread.ManagedThreadId);
4:      await SomeFastAsyncOperation().ConfigureAwait(false);
5:      Console.WriteLine("Current thread: " + 
                               Thread.CurrentThread.ManagedThreadId);
6:      await SomeSlowAsyncOperation();
7:      Console.WriteLine("Current thread: " + 
                           Thread.CurrentThread.ManagedThreadId);
8:  }

The first async operation uses ConfigureAwait(false). The second operation doesn’t use this option. Now you would think that the first Thread id is the UI thread and the second and third one are ids of different threads. This is true if your first operation actually executes asynchronously. But if that operation finishes really quickly, before you are awaiting it, the generated code is smart enough to not switch to another thread. That way, it could suddenly happen that the second operation on line 6 executes on the UI thread.

And that’s what I meant with the paragraph from the introduction of this article:

“Both awaits use the ConfigureAwait(false) method because if the first method is already finished before the awaiter checks, the code still runs on the UI thread”

Meaning that your code should look like this:

1:  private async void button1_Click(object sender, EventArgs e)
2:  {
3:      Console.WriteLine("Current thread: " + Thread.CurrentThread.ManagedThreadId);
4:      await SomeFastAsyncOperation().ConfigureAwait(false);
5:      Console.WriteLine("Current thread: " + Thread.CurrentThread.ManagedThreadId);
6:      await SomeSlowAsyncOperation().ConfigureAwait(false);
7:      Console.WriteLine("Current thread: " + Thread.CurrentThread.ManagedThreadId);
8:  }
9:
10:  private Task<string> SomeFastAsyncOperation()
11:  {
12:      return Task.FromResult("test");
13:  }
14:
15:  private async Task<string> SomeSlowAsyncOperation()
16:  {
17:      HttpClient client = new HttpClient();
18:      string result = await client.GetStringAsync("http://microsoft.com");
19:      return result;
20:  }

As you can see, both operations on line 4 and 6 use the ConfigureAwait(false) method. So although the operation on line 12 finishes immediately, the method on line 15 will still be configured correctly.

If you copy this code to your WinForms application you can play around with it by changing the values for ConfigureAwait. Also try to access the label element in combination with those changes so you can clearly see the results.

What do you think about async and await? Have you already used ConfigureAwait or do you have other questions about it? Please leave a comment!

Wouter de Kort works as a lead architect and consultant. He helps organizations stay on the cutting edge of software development. Wouter focuses on DevOps. He loves solving complex problems and helping other developers to grow. Wouter authored the book DevOps on the Microsoft stack and a couple of other books. Wouter is a Microsoft MVP and an ALM Ranger. You can find him on Twitter (@wouterdekort), on his blog at wouterdekort.com and at the various conferences where Wouter speaks.

Share

Leave a Reply

Your email address will not be published. Required fields are marked *

Post comment

This site uses Akismet to reduce spam. Learn how your comment data is processed.