Blog Post

C# is a great programming language and it continues to evolve at a good pace even 15 years after it’s introduction.

One of the best features to ever be added to C# / .NET was async / await.

Early writers of asynchronous code used IAsyncResult and after that came the Task Parallel Library. But the TPL was still relatively difficult to use so most programmers only wrote asynchronous code when they really had to.

And then along came async / await which made our lives so much easier. Everyone and their dog can write asynchronous code. Hidden in that simplicity, however, is a lot of complexity and a few assumptions.

Today, we’re going to be talking about one of the biggest gotchas… It’s a suprising detail that a lot of experienced .NET developers don’t know about. It’s about await and the synchronization context.

Await and the UI thread

Today’s discussion will be in the context of a Xamarin Android application. However, this information is just as applicable in any type of app that has a sychronization context (WPF, ASP.NET etc).

Consider the following code:

    _swipeRefreshLayout.Refresh += async delegate
    {
        var photos = await _photoService.GetPhotos ();

        _listView.Adapter = new PhotoAdapter(this, photos);

        _swipeRefreshLayout.Refreshing = false;
    };

This code does the following:

  1. calls a service which will eventually return a list of Photo

  2. binds the Photo objects to a list view

  3. tells the UI to stop the swipe refresh indicator

The call to GetPhotos returns a Task.

The task will return almost immediately and a background thread will go fetch a large amount of data.

Because of the use of await keyword, the UI thread will be released to go and do other UI work while the background thread is fetching the data. This keeps the UI nice and responsive.

What happens when the list of Photo returns on the background thread? Some UI behaviours: the list view is filled with photos and the swipe refresh spinner is stopped.

We all know that UI work has to be done on the UI thread, right? So where is the code that says “do this on the UI thread”?

I didn’t write that code! The compiler assumes I want the continuation after the await line to happen on the UI thread and it sorts out the transition from background thread to UI thread for me. Magic!

More specifically, the code generated around await captures the synchronization context (a.k.a. the UI thread) and, if set, requests that all further code is executed on that synchronization context.

Let’s just absolutely verify that the line after the await in on the UI thread. In the IDE, I can set a breakpoint on that line and then look at the Threads inspector:

Continuation on UI thread

See that thread 1 is bold when we’re on the breakpoint? Thread 1 is the UI thread.

This is very safe behaviour. I can use async / await and never know about the synchronization context or worry about the UI thread.

But is this the behaviour I always want?

Non-UI work on the UI thread

While that behaviour creates safe code, it’s not always the best code.

Consider the following implementation of GetPhotos:

    public class PhotoService
    {
        public async Task<List<Photo>> GetPhotos()
        {
            var client = new WebClient ();

            var photosAsJson = await client.DownloadStringTaskAsync ("http://jsonplaceholder.typicode.com/photos");

            var photos = JsonConvert.DeserializeObject<List<Photo>>(photosAsJson);

            return photos.OrderBy (c => c.Title).ToList ();
        }
    }

This code does the following:

  1. calls a service to download a JSON BLOB of data

  2. deserializes the JSON BLOB into Photo objects

  3. sorts the Photo objects by title

The behaviour we applied in the previous section also applies here…

The call to GetPhotos may or may not be on the UI thread. In the case of our app the call is on the UI thread.

Downloading the JSON will happen on a background thread in the call to DownloadStringTaskAsync(). This method will return almost immediately with a Task. The calling thread (in this case the UI thread) will be released and free to do other work.

At some point, the call to DownloadStringTaskAsync will return on a background thread. Because await is specified, the synchronization context is captured (the UI thread in our case) and the remainder of the code (deserialization and sorting) is scheduled on that thread.

But does the deserialization and the sorting really need to be done on the UI thread? Absolutely NOT!

In the case of this app, the code above will actually make the app appear to be unresponsive for a second or two while the deserialization and sorting are done.

I start the the app and swipe the list view to refresh it… the indicator moves around for a sec or two and then mysteriously freezes:

Swipe Indicator Frozen

When I check the application log, I see this warning:

[Choreographer] Skipped 199 frames! The application may be doing too much work on its main thread.

How do we fix this? We want the deserialization and sorting to be done on a background thread!

So how do we tell .NET to not use the synchronization context in the implementation of GetPhotos at the await?

ConfigureAwait(false)

There’s a secret little method on Task<T> called ConfigureAwait:

    public new ConfiguredTaskAwaitable<TResult> ConfigureAwait (bool continueOnCapturedContext);

I would argue that this isn’t a great name. If the creators of this method would have called the it DontDoThisOnTheUiThread() I think more people would discover this feature themselves

The default behaviour is ConfigureAwait(true).

If I don’t want the code to capture the synchronization context on the completion of the awaited task, I use ConfigureAwait(false) like so:

    public class PhotoService
    {
        public async Task<List<Photo>> GetPhotos()
        {
            var client = new WebClient ();

            var photosAsJson = await client.DownloadStringTaskAsync ("http://jsonplaceholder.typicode.com/photos")
                .ConfigureAwait (false);

            var photos = JsonConvert.DeserializeObject<List<Photo>>(photosAsJson);

            return photos.OrderBy (c => c.Title).ToList ();
        }
    }

If I run the application now and check out the Threads inspector after the await call, I’ll see the deserialization and sorting are running on a thread pool thread, not the UI thread (thread 1):

Continuation on background thread

All the way up. Almost…

The last thing to keep in mind for ConfigureAwait(false) is that it affects only the continuation of the single await where it’s used.

To say it another way, you should specify ConfigureAwait(false) all the way up the call stack until you get to some UI code.

Consider the following hypothectical code:

    protected async override void OnCreate (Bundle savedInstanceState)
    {
        base.OnCreate (savedInstanceState);

        SetContentView (Resource.Layout.Main);

        _listView = FindViewById<ListView>(Resource.Id.listView);

        _swipeRefreshLayout = FindViewById<SwipeRefreshLayout>(Resource.Id.swipeRefreshLayout);
        _swipeRefreshLayout.Refresh += async delegate
        {
            var photos = await GetAndSortPhotos ();

            _listView.Adapter = new PhotoAdapter(this, photos);
            _swipeRefreshLayout.Refreshing = false;
        };
    }

    async Task<List<Photo>> GetAndSortPhotos()
    {
        var photos = await _photoService.GetPhotos ();

        return photos.OrderBy (c => c.Title).ToList ();
    }

    async Task<List<Photo>> GetPhotos()
    {
        var client = new WebClient ();

        var photosAsJson = await client.DownloadStringTaskAsync ("http://jsonplaceholder.typicode.com/photos")
            .ConfigureAwait (false);

        var photos = JsonConvert.DeserializeObject<List<Photo>>(photosAsJson);
    }

Since ConfigureAwait(false) is specified on line 31, the deserialization on line 33 is scheduled on a background thread.

But that is only 1 of 3 await in this code. Each of the 3 await will capture the synchronization context.

What about the sorting of the photos? The await on line 21 will also capture the synchronization context and the sorting on line 23 is scheduled on the UI thread because there is no ConfigureAwait(false) specified.

So there should be a ConfigureAwait(false) on line 21 too!

What about the await on line 12?

Well the code below that does modify the UI, so in that case we don’t wait to specify ConfigureAwait(false) there. That would result in an exception being thrown!

Wrap Up

I recently worked on a Xamarin Forms app that supported iOS7 and up. The app worked great on an iPhone 6… but users were complaining the it was freezing on an iPhone 4. Yes, there are users who still use iPhone 4’s!

When I looked into the issue, I discovered it was because of this very issue. The app was doing too much work on the UI thread because no ConfigureWait(false) was specified anywhere.

On an iPhone 6, the phone is powerful enough that it only affected the experience slightly. But on an older, underpowered phone the extra work that the UI thread was doing degraded the experience massively.

It’s unfortunate that ConfigureAwait(false) is named so. Additionally, it really makes my beautiful await code suddenly look very ugly. It’s a shame they just didn’t invent another keyword for it, like awaitb or something.

That being said, you should definitely be using ConfigureAwait where appropriate. Your users will thank you for it!


Here’s a great MSDN article describing this, and other, asynchronous best practices:

https://msdn.microsoft.com/en-us/magazine/jj991977.aspx

Sample Android application on github.