27

I've read about ConfigureAwait in various places (including SO questions), and here are my conclusions:

  • ConfigureAwait(true): Runs the rest of the code on the same thread the code before the await was run on.
  • ConfigureAwait(false): Runs the rest of the code on the same thread the awaited code was run on.
  • If the await is followed by a code that accesses the UI, the task should be appended with .ConfigureAwait(true). Otherwise, an InvalidOperationException will occur due to another thread accessing UI elements.

My questions are:

  1. Are my conclusions correct?
  2. When does ConfigureAwait(false) improve performance, and when does it not?
  3. If I am writing for a GUI application but the next lines don't access the UI elements, should I use ConfigureAwait(false) or ConfigureAwait(true) ?
pjpscriv
  • 866
  • 11
  • 20
Youssef13
  • 3,836
  • 3
  • 24
  • 41
  • 3
    I do not recommend using SO as the primary source of info about async/await. there is a lot of misinformation here by people who think they know how it works. Use the documentation and writings of Stephen Toub, Stephen Cleary, Jon Skeet. But to go against my own advice, you might find [one of my blog posts helpful](https://contrivedexample.com/2017/12/04/configureawait-sometimes-saves-the-day-everytime/). – Crowcoder Jul 01 '20 at 17:03
  • 1
    Does this answer your question? [How and when to use ‘async’ and ‘await’](https://stackoverflow.com/questions/14455293/how-and-when-to-use-async-and-await) – Michael Jul 01 '20 at 17:04
  • 1
    @MichaelSearson It doesn't explain the performance part. – Youssef13 Jul 01 '20 at 17:06
  • 3
    `Are my conclusions correct` - no. Async is not about threads. There is no *direct* correspondence between `ConfigureAwait` and the use of a certain thread. See e.g. https://stackoverflow.com/q/46094134/11683. – GSerg Jul 01 '20 at 17:08
  • 1
    @GSerg From my understanding, the point of ConfigureAwait is whether the following code in the method executes on the main thread or not. In cases where UI elements are accessed after awaiting a task, it may throw InvalidOperationException if the task is called with ConfigureAwait(false). The reason from my understanding is that UI updates were run from a different thread. If the conclusions are wrong, please provide more details. – Youssef13 Jul 01 '20 at 17:12
  • 2
    You have to define what you mean by "performance". Async code is actually slightly less performant than the same, non-async code. The main benefits are in scalability and parallelism, especially with I/O bound code. – Crowcoder Jul 01 '20 at 17:15
  • 2
    @Youssef13 Async is concerned with synchronization contexts, not threads. Using or not using a separate thread is an implementation detail, unless explicitly documented otherwise (e.g. with `Task.Run()`). `ConfigureAwait(false)` *premits* resuming without capturing the synchronization context, not *requires* it. For some synchronization contexts (e.g. the one used in a Winforms application), "resuming on the same context" means "running on the UI thread". For other contexts it may not mean that, or may have no observable effect at all. – GSerg Jul 01 '20 at 17:16
  • 1
    @Crowcoder I'm not comparing asynchronous code with synchronous one. I'm comparing two async codes. One with ConfigureAwait(true) and the other with ConfigureAwait(false). [This article](https://devblogs.microsoft.com/dotnet/configureawait-faq/) states that ConfigureAwait(false) improves performance. My question is: when does it actually improves it, and when it doesn't? – Youssef13 Jul 01 '20 at 17:16
  • 2
    @Youssef13 yes, good you found Stephen Toub. He talks about "hot path" which probably doesn't apply to you, that performance gain would likely be too small to measure (just an assumption without seeing your code). Do not use `ConfigureAwait(false)` for "context-aware" code, like something that must execute on the UI thread after completion. Another example is accessing HttpContext in an ASP.NET WebForms application. The link I posted points to a WPF application that explores these things, including how you can still deadlock when using ConfigureAwait(false) with sloppy library code. – Crowcoder Jul 01 '20 at 17:24
  • 1
    For more information about ConfigureAwait and concurrency i recommend the book Concurrency in C# Cookbook by Stephen Cleary – Vlad Jul 01 '20 at 17:58

3 Answers3

30

To answer your questions more directly:

ConfigureAwait(true): Runs the rest of the code on the same thread the code before the await was run on.

Not necessarily the same thread, but the same synchronization context. The synchronization context can decide how to run the code. In a UI application, it will be the same thread. In ASP.NET, it may not be the same thread, but you will have the HttpContext available, just like you did before.

ConfigureAwait(false): Runs the rest of the code on the same thread the awaited code was run on.

This is not correct. ConfigureAwait(false) tells it that it does not need the context, so the code can be run anywhere. It could be any thread that runs it.

If the await is followed by a code that accesses the UI, the task should be appended with .ConfigureAwait(true). Otherwise, an InvalidOperationException will occur due to another thread accessing UI elements.

It is not correct that it "should be appended with .ConfigureAwait(true)". ConfigureAwait(true) is the default. So if that's what you want, you don't need to specify it.

  1. When does ConfigureAwait(false) improves performance, and when it doesn't?

Returning to the synchronization context might take time, because it may have to wait for something else to finish running. In reality, this rarely happens, or that waiting time is so minuscule that you'd never notice it.

  1. If writing for a GUI application, but the next lines doesn't access the UI elements. Should I use ConfigureAwait(false) or ConfigureAwait(true) ?

You could use ConfigureAwait(false), but I suggest you don't, for a few reasons:

  1. I doubt you would notice any performance improvement.
  2. It can introduce parallelism that you may not expect. If you use ConfigureAwait(false), the continuation can run on any thread, so you could have problems if you're accessing non-thread-safe objects. It is not common to have these problems, but it can happen.
  3. You (or someone else maintaining this code) may add code that interacts with the UI later and exceptions will be thrown. Hopefully the ConfigureAwait(false) is easy to spot (it could be in a different method than where the exception is thrown) and you/they know what it does.

I find it's easier to not use ConfigureAwait(false) at all (except in libraries). In the words of Stephen Toub (a Microsoft employee) in the ConfigureAwait FAQ:

When writing applications, you generally want the default behavior (which is why it is the default behavior).

Edit: I've written an article of my own on this topic: .NET: Don’t use ConfigureAwait(false)

Gabriel Luci
  • 38,328
  • 4
  • 55
  • 84
  • 3
    Hi Gabriel, I am glad that you are alive! You disappeared from SO for a year or so. I missed the image of the peaceful cow in the grass lands. – Theodor Zoulias Jun 10 '21 at 16:49
  • 4
    @TheodorZoulias Hey! I'm surprised you noticed. I've been lurking, but the past year has been hectic and stressful, so I haven't been taking the time to answer any questions. – Gabriel Luci Jun 11 '21 at 02:06
3

ConfigureAwait(false) may improve performance if there are not many worker threads available and if the thread that it would need to wait for is constantly busy.

ConfigureAwait(false) is recommended everywhere where coming back to same SynchronizationContext (which usualy is linked with thread) is not needed, especially in libraries that awaits something internally: https://medium.com/bynder-tech/c-why-you-should-use-configureawait-false-in-your-library-code-d7837dce3d7f.

ConfigureAwait(true) (which is the default) is needed when you require same context but may also lead to a dead lock in certain situations.

Consider this code:

void Main()
{
    // creating a windows form attaches a synchronization context to the current thread
    new System.Windows.Forms.Form();
    var task = DoSth();
    Console.WriteLine(task.Result);
}

async Task<int> DoSth()
{
    await Task.Delay(1000);
    return 1;
}

in this example because of not awaited task DoSth, the main UI thread is blocked by waiting for task.Result - at the same time DoSth is blocked because it wants to come back to the UI thread after a delay. This will lead to a deadlock and this code will never execute to the end. Adding .ConfigureAwait(false) solves the problem in this case.

Adassko
  • 5,201
  • 20
  • 37
  • 2
    Saying that ConfigureAwait(true) is recommended in libraries confuses me more. – Youssef13 Jul 01 '20 at 17:07
  • @Youssef13: this is because you cannot control if the developer will use your library in the proper way. By adding `.ConfigureAwait(false)` in library internal calls you're preventing potential deadlocks when the developer wants to use your library in synchronous way (using .Result). Of course tasks shouldn't be used in this way, but it will be your library that causes the problem in the eyes of user. – Adassko Jul 01 '20 at 17:18
  • @Adassko, You stated ConfigureAwait(true) in your answer. Maybe a typo? – Youssef13 Jul 01 '20 at 17:20
  • @Youssef13 yes, I wanted to write about `(true)` but removed it and started typing about `(false)`. My mistake – Adassko Jul 01 '20 at 17:22
  • @Adassko, So, should I **always** use ConfigureAwait(false) if coming back to the same SynchronizationContext isn't needed? Does it **always** improve performance? If not, when it doesn't improve performance? (I didn't get the first line of the answer) – Youssef13 Jul 01 '20 at 17:27
  • 3
    Recommending `.ConfigureAwait(false)` as a cure for deadlocks is [not good](https://blog.stephencleary.com/2012/07/dont-block-on-async-code.html#preventing-the-deadlock). – GSerg Jul 01 '20 at 17:28
  • I'm still confused and can't get sense as when to use false and when to use true. The way I **currently** think is, use false always whenever possible (it's possible for library code). And use true if you need to get back to the same SynchronizationContext. But not sure for a case when I'm in a GUI app but next lines don't access UI elements. – Youssef13 Jul 01 '20 at 17:33
  • @Youssef13 you shouldn't care about it too much. It's nice to add it to libraries to help prevent deadlocks. ConfigureAwait(false) is never slower and can be used always when context is not needed - there are even solutions to add it always to all calls like this fody plugin: https://github.com/Fody/ConfigureAwait . But I wouldn't care about a performance at all unless it's an issue for you. false may improve performance significantly only when the synchronizationcontext thread is really busy and it takes time to wait for it which is almost never a case – Adassko Jul 01 '20 at 17:38
  • @Youssef13 If you don't know how to use it, just ignore that `ConfigureAwait` exists (except in libraries). In regular applications, you will rarely ever actually need it. I never use it. Any performance improvement will not be noticeable. I wrote a [lengthy answer about this recently](https://stackoverflow.com/a/62505101/1202807). – Gabriel Luci Jul 02 '20 at 12:47
0

Using ConfigureAwait(false) in application code is normally not going to boost your application's performance in any meaningful way, because normally you don't await inside loops in application code. For example lets consider the case that your app has a button, and an async operation is started everytime the user clicks the button, and the async operation includes a single await. By typing the 22 characters .ConfigureAwait(false) after this await you have already lost comparable time of your life, with the time you can hope to save from 10 users that click this button once every minute, 8 hours per day, for 20 years each (~35,000,000 context switchings in total = some seconds of CPU processing time).

And this before taking into account the time you need to think about whether you can safely include this configuration (depending on whether the continuation contains UI-related code), the time you'll need in order to reconfirm you previous assessment every time you have to maintain/modify the code, and the time you'll lose on debugging in case your assessment was wrong.

On the other hand if your Button_Click handler contains code like this:

private async void Button_Click(object sender, EventArgs e)
{
    var client = new WebClient();
    using var stream = await client.OpenReadTaskAsync("someUrl");
    var buffer = new byte[1024];
    while ((await stream.ReadAsync(buffer, 0, buffer.Length)) > 0)
    {
        //...
    }
}

...then by all means do spend the extra time to ConfigureAwait(false) the ReadAsync task. Also do consider refactoring the code by moving the stream-reading part to a separate asynchronous method, so that you can safely access UI elements anywhere inside the Button_Click handler, without been distracted by technicalities that don't belong to this layer of the app.

Theodor Zoulias
  • 34,835
  • 7
  • 69
  • 104