Using ConfigureAwait(false)

I'm looking at the sample code. I was surprised that first ConfigureAwait(false) is called on httpClient.GetStringAsync, and then on sourceStream.WriteAsync. As far as I know, ConfigureAwait(false) indicates that the code should continue to be executed not in the context of UI, but in the context of the task. Why then call it 2 times?

private async void Button_Click(object sender, RoutedEventArgs e)
{
    HttpClient httpClient = new HttpClient();
    //до этого момента всё выполняется в UI контексте?
    string content = await httpClient.GetStringAsync("http://www.microsoft.com").
        ConfigureAwait(false); 
    //после выполнения верхней строчки остальной код который внизу будет выполняться в контексте веррхнего таска?
    using (FileStream sourceStream = new FileStream("temp.html", FileMode.Create, 
        FileAccess.Write, FileShare.None, 4096, useAsync: true))
    {
        byte[] encodedText = Encoding.Unicode.GetBytes(content);
        await sourceStream.WriteAsync(encodedText, 0, encodedText.Length).
            ConfigureAwait(false);
       //будь дальше какой-то код, в контексте какого потока он выполнялся б?
    };
}
Author: Lightness, 2017-06-20

5 answers

See.

ConfigureAwait(false) means, indeed, "I don't care in which stream SynchronizationContext'e will be executed by the tail of the method".

That is, the first ConfigureAwait(false) can send the" tail " of the method to the background thread. But exactly what can, and not should! If for some reason the first task is executed synchronously (for example, the string is already in the cache), then the translation to another SynchronizationContext is performed will not be, and the execution will continue in the original one context.

If the second await is not provided with the ConfigureAwait(false) construction, then the tail of the method will be executed again in the original context - that is, in your case, in the UI context.

Thus, for library methods that do not communicate with the UI, it is practically necessary to add ConfigureAwait(false)to each internal await'y'.]}


It is clear that adding to each of the await's ConfigureAwait(false) is a bit lazy. You can use this trick instead: "escape" to the pool threads at the very beginning, and don't worry about it anymore. This can be done using this construction:

private async void Button_Click(object sender, RoutedEventArgs e)
{
    await AsyncHelper.RedirectToThreadPool();
    // всё, мы больше не в UI-контексте, гарантировано

    HttpClient httpClient = new HttpClient();
    string content = await httpClient.GetStringAsync("http://www.microsoft.com"); 
    // ...
}

Auxiliary classes (taken from here):

static class AsyncHelper
{
    public static ThreadPoolRedirector RedirectToThreadPool() =>
        new ThreadPoolRedirector();
}

public struct ThreadPoolRedirector : INotifyCompletion
{
    // awaiter и awaitable в одном флаконе
    public ThreadPoolRedirector GetAwaiter() => this;

    // true означает выполнять продолжение немедленно 
    public bool IsCompleted => Thread.CurrentThread.IsThreadPoolThread;

    public void OnCompleted(Action continuation) =>
        ThreadPool.QueueUserWorkItem(o => continuation());

    public void GetResult() { }
}

(the idea is taken from Stephen Toub await anything;)

 27
Author: VladD, 2017-06-20 10:36:57

A little theory:

When using the keyword await, the compiler does a lot of interesting things, but in this case, we are interested in what happens when remembering (in fact, other contexts are also remembered) of the synchronization context SynchronizationContext, which is intended for executing code in a specific type of thread. The SynchronizationContext class has an important Post method that ensures that the passed delegate is executed in the correct context.

So, we remember that the code preceding the first one await, executed in the calling thread, but what happens when the execution of your method resumes after await? In fact, in most cases it is also executed in the calling thread, despite the fact that the calling thread may have been doing something else in the interim. To achieve this effect, the current context SynchronizationContext is saved (this happens when the await operator is encountered). Next, when the method when it resumes, the compiler inserts a call to Post so that execution resumes in the stored context. As a rule, calling this method is relatively expensive. Therefore, to avoid overhead, .NET does not call Post if the stored synchronization context matches the current one at the time of task completion. However, if the synchronization contexts are different, then an expensive Post call is required. If performance comes first or it's about in a library code that doesn't care which thread it runs on, it probably doesn't make sense to incur such costs. Therefore, in this case, you should call the ConigureAwait(false) method before waiting for it. It is important to understand that this method is intended as a way to inform .NET that you do not care in which thread the execution of is resumed. If this thread is not very important, for example, it is taken from a pool, then the code execution in it will continue. But if the flow is for some reason If it is important, then .NET will prefer to release it for other tasks, and continue executing your method in a thread taken from the pool. The decision about whether a thread is important or not is made based on an analysis of the current synchronization context.


This was an introduction, and now we will slightly modernize your example. The functionality responsible for getting content from the site www.microsoft.com will be placed in a separate method. Note that ConigureAwait(false) is no longer used here.

 public async Task<string> GetContentAsync()
 {
     HttpClient httpClient = new HttpClient();
     string content = await httpClient.GetStringAsync("http://www.microsoft.com");
     return content;
 }

Next, slightly change click event handler:

private async void Button_Click(object sender, RoutedEventArgs e)
{
    // Обратите внимание, что здесь мы не используем оператор `await`
    // Кроме того, все что идет ниже, нам уже не интересно, так как мы попали в deadlock
    var content = GetContentAsync().Result;

    using (FileStream sourceStream = new FileStream("temp.html", FileMode.Create, 
    FileAccess.Write, FileShare.None, 4096, useAsync: true))
    {
        byte[] encodedText = Encoding.Unicode.GetBytes(content);
        await sourceStream.WriteAsync(encodedText, 0, encodedText.Length).
        ConfigureAwait(false);
    };
}

What is happening here and why does the deadlock occur?

  1. Calling the Result property blocks the calling thread until the GetContentAsync asynchronous operation is completed.

  2. So in the GetContentAsync method, the keyword await is used, the current SynchronizationContext context is saved in this case UI.

  3. After the GetContentAsync method is executed, you will need to resume the Button_Click method in saved context SynchronizationContext, but this will not work because the main thread is in standby mode due to a call Result.

The actual summary:

If performance comes first, or if you are talking about library code that doesn't care which thread it runs on, you should use ConigureAwait(false).

 15
Author: sp7, 2017-06-20 09:24:21

Subsequent calls to ConfigureAwait(false) do not affect the synchronization context in any way. The method is still executed in a non-UI thread.

But that's what I do in my code, too. This is done as a rule of good taste. So that if you delete one of the await-constructs, the method does not break.

 7
Author: Vadim Ovchinnikov, 2017-06-20 15:58:04

To simplify working with ConfigureAwait(false) You can use

Fody ConfigureAwait

Your code

using Fody;

[ConfigureAwait(false)]
public class MyAsyncLibrary
{
    public async Task MyMethodAsync()
    {
        await Task.Delay(10);
        await Task.Delay(20);
    }

    public async Task AnotherMethodAsync()
    {
        await Task.Delay(30);
    }
}

What gets compiled

public class MyAsyncLibrary
{
    public async Task MyMethodAsync()
    {
        await Task.Delay(10).ConfigureAwait(false);
        await Task.Delay(20).ConfigureAwait(false);
    }

    public async Task AnotherMethodAsync()
    {
        await Task.Delay(30).ConfigureAwait(false);
    }
}
 3
Author: Serginio, 2017-06-21 13:29:17

In this example, just a programmer with experience with tasks applied an outdated approach for await:

private Task<string> ReadFileAsync() // no async key word. Need to warry about context and threads and use ConfigureAwait(false)
{
    return Task.Run(() => 
    {
        //some async work
    });
}
//... 
Task<string> result = ReadFileAsync.ConfigureAwait(false);

Most likely, it was a mixture of 2 approaches that led astray. The ConfigureAwait method in your example is simply not needed, it does not play a role at all, because there is already an await for which a separate asynchronous wrapper will be generated, the method will return a Task and only then wait for the result.

All of this will be returned to the thread that called the asynchronous task. Problem it would only be possible if the calling thread was doing some other long-running task at that time. Then, to save time, we would use ConfigureAwait, which would sound like " Already back? Take any free thread, forget about the context and work on" but this is basically not possible in such code. Here is actually 1 task and we are waiting for its completion.

And your code would have to look right like this:

private async void Button_Click(object sender, RoutedEventArgs e)
{
    HttpClient httpClient = new HttpClient();
    string content = await httpClient.GetStringAsync("http://www.microsoft.com");

    using (FileStream sourceStream = new FileStream("temp.html", FileMode.Create, 
        FileAccess.Write, FileShare.None, 4096, useAsync: true))
    {
        byte[] encodedText = Encoding.Unicode.GetBytes(content);
        await sourceStream.WriteAsync(encodedText, 0, encodedText.Length);
        // весь код после WriteAsync выполняется в контексте UI, но на время, пока будет выполняться асинхронный код WriteAsync, поток UI благодаря await не будет ждать ответа (не будет зависания) и вернётся только после завершения асинхронной задачи (await). Внутри компилятором будет сгенерирован код, который это и сделает.
    };
}

Here is a similar one the situation is described in the book for the microsoft exam 70-843 on page 26. It is a much more reliable source than articles on the Internet, where everything is collected in a bunch and async \ await and ConfigureAwait ():

private async void StartButton_Click(object sender, RoutedEventArgs e)
{
  long noOfValues = long.Parse(NumberOfValuesTextBox.Text);
  ResultTextBlock.Text = "Calculating";
  double result = await (asyncComputeAverages(noOfValues));
  ResultTextBlock.Text = "Result: " + result.ToString();
}

The keyword precedes a call of a method that will return the task to be performed. The compiler will generate code that will cause the async method to return to the caller at the point the await is reached. It will then go on to generate code that will perform the awaited action asynchronously and then continue with the body of the async method. In the case of the Button_Click method in Listing 1-32, this means that the result is displayed upon completion of the task returned by asyncComputeAverages. The code does not block the user interface when it runs, the display is updated on the correct thread (the original event handler), and it is very simple to use.

Or on habr

PS

I'll try to explain on the example of the coach, players, and balls. Where the ball is the flow. The coach talks to the player (synchronous execution). The coach can kick the ball and say run. So he can do for all 11 players with 11 balls-streams. But in your code, it does the following:

  1. talking
  2. kicks the ball.
  3. waits until the player brings it, while he is busy with other things, does not take the ball.
  4. then talks
  5. then kicks the ball in the other direction. the side.

ConfigureAwait(false) would be needed for the case:

  1. the coach talks to the first player
  2. kicks the ball, the first player runs away, taking the flow-ball
  3. the coach talks to the second player
  4. at this time, the first one came running, waiting for a new blow. But the coach is still talking to the 2nd player.
  5. So in the case of ConfigureAwait (false), the player takes any free ball and, without distracting the coach, continues training (he hits the ball and runs away.) Because he no longer cares about the context (coach's instructions) or he wrote everything down on a piece of paper(saved the context)
 0
Author: Artem G, 2019-09-10 12:16:50