tl;dr
Yes, it is needed in order to ensure that all of your asynchronous continuations within your library code are executed on a thread pool thread (depending on the SynchronizationContext/TaskScheduler in use).
Would you like to know more?
Task.ConfigureAwait(Boolean)
true
attempts to marshal the remainder of the async method back to the original context captured
false
schedules the remainder of the async method on a thread pool thread
Consider the following WPF example:
WPF uses the DispatcherSynchronizationContext to resume asynchronous continuations on the UI context, because a background thread cannot update the contents of Controls.
private async void Button_Click(object sender, RoutedEventArgs e)
{
logger.LogInformation(Thread.CurrentThread.IsThreadPoolThread); //false -> GUI context
await CompleteAsynchronously();
logger.LogInformation(Thread.CurrentThread.IsThreadPoolThread); //false -> GUI context
await CompleteAsynchronously().ConfigureAwait(false);
logger.LogInformation(Thread.CurrentThread.IsThreadPoolThread); //true
}
private async Task CompleteAsynchronously()
{
logger.LogInformation(Thread.CurrentThread.IsThreadPoolThread); //false -> GUI context
await Task.Delay(TimeSpan.FromMilliseconds(100)).ConfigureAwait(false);
logger.LogInformation(Thread.CurrentThread.IsThreadPoolThread); //true
}
Here, you see that the continueOnCapturedContext
flag of the called method has no effect on the caller. Yet, the called method runs (or at least starts to run) on the thread that the caller was running on, of course.
However, the capturing of the current context (either the current SynchronizationContext
; if null then the current TaskScheduler
) only occurs when an incomplete Task is awaited. If the Task completes synchronously, continueOnCapturedContext
has no effect and the remainder of the method continues to run synchronously on the current thread.
private async void Button_Click(object sender, RoutedEventArgs e)
{
logger.LogInformation(Thread.CurrentThread.IsThreadPoolThread); //false -> GUI context
await CompleteSynchronously().ConfigureAwait(false);
logger.LogInformation(Thread.CurrentThread.IsThreadPoolThread); //false -> GUI context
}
private async Task CompleteSynchronously()
{
await Task.Delay(0);
}
So, in your library code (assuming you never require context), you should always use ConfigureAwait(false)
in order to ensure that no context is captured for asynchronous continuations, regardless of the framework calling into your assemblies (e.g. WPF, ASP.NET Core, Console, ...).
For more details, have a look at Best Practices in Asynchronous Programming (i.a. ConfigureAwait
) in this MSDN Magazine Article by Stephen Cleary.