Why would I bother to use Task.ConfigureAwait(continueOnCapturedContext: false);
Asked Answered
B

2

90

Consider the following code of windows forms:

private async void UpdateUIControlClicked(object sender, EventArgs e)
{
    this.txtUIControl.Text = "I will be updated after await - i hope!";
    await Task.Delay(5000).ConfigureAwait(continueOnCapturedContext: false);
    this.txtUIControl.Text = "I am updated now.";
}

Here the exception is thrown at the 3rd line because after await the code is executed on non-UI thread. Where ConfigureAwait(false) is useful?

Bidden answered 8/1, 2015 at 22:57 Comment(2)
Okay - in non UI environment, it doesnt matter what thread continues the execution of the rest of the method. Is there any performance hit when using false? May be await needs to do more work (SynchronisationContext) if its is forced to execute the continuation on the original thread.Bidden
Read this post. Has great detailGigantic
B
129

Stephen Cleary has a really good series on this you can find here, I quoted the piece specific to your question:

Most of the time, you don’t need to sync back to the “main” context. Most async methods will be designed with composition in mind: they await other operations, and each one represents an asynchronous operation itself (which can be composed by others). In this case, you want to tell the awaiter to not capture the current context by calling ConfigureAwait and passing false, e.g.:

private async Task DownloadFileAsync(string fileName)
{
  // Use HttpClient or whatever to download the file contents.
  var fileContents = await DownloadFileContentsAsync(fileName).ConfigureAwait(false);

  // Note that because of the ConfigureAwait(false), we are not on the original context here.
  // Instead, we're running on the thread pool.

  // Write the file contents out to a disk file.
  await WriteToDiskAsync(fileName, fileContents).ConfigureAwait(false);

  // The second call to ConfigureAwait(false) is not *required*, but it is Good Practice.
}

// WinForms example (it works exactly the same for WPF).
private async void DownloadFileButton_Click(object sender, EventArgs e)
{
  // Since we asynchronously wait, the UI thread is not blocked by the file download.
  await DownloadFileAsync(fileNameTextBox.Text);

  // Since we resume on the UI context, we can directly access UI elements.
  resultTextBox.Text = "File downloaded!";
}

The important thing to note with this example is that each “level” of async method calls has its own context. DownloadFileButton_Click started in the UI context, and called DownloadFileAsync. DownloadFileAsync also started in the UI context, but then stepped out of its context by calling ConfigureAwait(false). The rest of DownloadFileAsync runs in the thread pool context. However, when DownloadFileAsync completes and DownloadFileButton_Click resumes, it does resume in the UI context.

A good rule of thumb is to use ConfigureAwait(false) unless you know you do need the context.

Broadsword answered 8/1, 2015 at 23:32 Comment(7)
Thank you Victor Learned. It is clear that when we know that the code after await can be executed on any thread, we can use ConfigureAwait(continueOnCapturedContext: false); Something like another web service / IO operation is needed which can be done on any thread other than UI. Since we are in another thread than UI, to update UI control should we be using Control.BeginInvoke() / Invoke method?Bidden
If it's recommended to use ConfigureAwait(false), then why is it not default to false already?Oversight
@JohnC because it may produce errors (in some very special cases) - in contrast to ConfigureAwait(true) which will work all times. Feel free to check out the available fody addins that automatically change all awaiters in your code (so changing the default).Nordine
If the rest of DownloadFileAsync runs in the threadpool context, is the ConfigureAwait(false) necessary on WriteToDiskAsync?Fleeta
@Fleeta It's advisable, since you may not be able to guarantee what thread the first async runs in. If the code has a fast path, or is cached, it may execute synchronously, and thus within the current context, not the threadpool context. Which means the second async would also execute the current context and could lead to a deadlock. This answer has a good explanation.Majesty
Stephen Cleary's explanation that you referenced cleared up many pages of reading for me. The key was understanding that each async method has its own context. So in a library method you want ConfigureAwait(false), but the caller from a UI thread still has his UI context the outer await will return too, however they still have control to ConfigureAway(false) at that level as well if they don't really need to return to that. Thank you for posting this! Very helpful!Radford
Stephen Cleary's explanation is by far the best i have read on this. Thank you for posting this.Vest
D
17

You should use it at all times in services since services should be UI agnostic.

However, dont use it outside of services if

  • need to manipulate UI or use UI specific components such as Dispatcher or CoreDispatcher
  • need to use HttpContext.Current in ASP.net

In these cases, you should not use ConfigureAwait(continueOnCapturedContext: false) as it is important to capture current context otherwise app will crash with trying to access UI views from a non-UI thread

When you write await task;, that is equivalent to writing await task.ConfigureAwait(continueOnCapturedContext: true); because continueOnCapturedContext is true by default.

Derwin answered 21/5, 2019 at 21:3 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.