Tuesday, October 1, 2013

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 this 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):

   1:  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!

3 comments:

  1. Hi Wouter,

    Thanks for your explanation, you clarify this topic for me, thanks. Again great work!

    I have another question that maybe you can give me a light. It's about the IFormattable interface, in the book you say "IFormattable provides the functionality to format the value of an object into a string representation. It is also used by the Convert class to the opposite". When you say "It is also used by the Convert class to the opposite" means that it is possible to convert to an object from string with some method of Convert Class and implementing IFormattable interface? If it's so, what is that method? I've searched in google but I didn't find it.

    Thanks for your help!

    Kind regards,

    Nicolás

    ReplyDelete
    Replies
    1. Hi Nicholas,

      I've reread the section you mention and I have to admit I don't know what I was thinking when I wrote it :)

      As you pointed out, there is no way to let the Convert class parse a string to the object type you want. You do have the Convert.ChangeType (http://msdn.microsoft.com/en-us/library/system.convert.changetype.aspx) method which is worth checking out.

      If you want to be able to take a string representation of your object and turn it back into your object, you can create a Parse/TryParse method on your class. There you can put the custom logic to parse your string and return an object.

      Kind regards,

      Wouter

      Delete
  2. Wow this ini awsome, your topic helpfull me

    ReplyDelete