Best practice to call ConfigureAwait for all server-side code
Update: ASP.NET Core does not have a SynchronizationContext
. If you are on ASP.NET Core, it does not matter whether you use ConfigureAwait(false)
or not.
For ASP.NET "Full" or "Classic" or whatever, the rest of this answer still applies.
Original post (for non-Core ASP.NET):
This video by the ASP.NET team has the best information on using async
on ASP.NET.
I had read that it is more performant since it doesn't have to switch thread contexts back to the original thread context.
This is true with UI applications, where there is only one UI thread that you have to "sync" back to.
In ASP.NET, the situation is a bit more complex. When an async
method resumes execution, it grabs a thread from the ASP.NET thread pool. If you disable the context capture using ConfigureAwait(false)
, then the thread just continues executing the method directly. If you do not disable the context capture, then the thread will re-enter the request context and then continue to execute the method.
So ConfigureAwait(false)
does not save you a thread jump in ASP.NET; it does save you the re-entering of the request context, but this is normally very fast. ConfigureAwait(false)
could be useful if you're trying to do a small amount of parallel processing of a request, but really TPL is a better fit for most of those scenarios.
However, with ASP.NET Web Api, if your request is coming in on one thread, and you await some function and call ConfigureAwait(false) that could potentially put you on a different thread when you are returning the final result of your ApiController function.
Actually, just doing an await
can do that. Once your async
method hits an await
, the method is blocked but the thread returns to the thread pool. When the method is ready to continue, any thread is snatched from the thread pool and used to resume the method.
The only difference ConfigureAwait
makes in ASP.NET is whether that thread enters the request context when resuming the method.
I have more background information in my MSDN article on SynchronizationContext
and my async
intro blog post.
Should I use configure await in all methods or only in the first method?
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 capturedfalse
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.
Is ConfigureAwait(false) required on all levels of the async chain when no real I/O call is involved?
The continuations unlike a synchronous code which precedes an await
call, are executed in context of different call-stack calls. In particular they are scheduled for execution as separate delegates and there is no call-stack relationship between them. So specifying ConfigureAwait(false)
for very last continuation execution will take effect only for this specific continuation, other continuations will still execute using their individual scheduling configuration. That is, if your goal is to ensure not capturing a synchronization context by any continuation in your library (in order to prevent potential dead locks or any other reason) then you should configure all await
calls which have continuations with ConfigureAwait(false)
.
Why do i need to use ConfigureAwait(false) in all of transitive closure?
As my knowledge when we using
ConfigureAwait(false)
the rest of async method will be run in the thread pool.
Close, but there is an important caveat you are missing. When you resume after awaiting a task with ConfigureAwait(false)
, you will resume on an arbitrary thread. Take note of the words "when you resume."
Let me show you something:
public async Task<string> GetValueAsync()
{
return "Cached Value";
}
public async Task Example1()
{
await this.GetValueAsync().ConfigureAwait(false);
}
Consider the await
in Example1
. Although you are awaiting an async
method, that method does not actually perform any asynchronous work. If an async
method doesn't await
anything, it executes synchronously, and the awaiter never resumes because it never suspended in the first place. As this example shows, calls to ConfigureAwait(false)
may be superfluous: they may have no effect at all. In this example, whatever context you were on when you enter Example1
is the context you will be on after the await
.
Not quite what you expected, right? And yet, it's not altogether unusual. Many async
methods may contain fast paths that don't require the caller to suspend. The availability of a cached resource is a good example (thanks, @jakub-dąbek!), but there plenty of other reasons an async
method might bail early. We often check for various conditions at the beginning of a method to see if we can avoid doing unnecessary work, and async
methods are no different.
Let's look at another example, this time from a WPF application:
async Task DoSomethingBenignAsync()
{
await Task.Yield();
}
Task DoSomethingUnexpectedAsync()
{
var tcs = new TaskCompletionSource<string>();
Dispatcher.BeginInvoke(Action(() => tcs.SetResult("Done!")));
return tcs.Task;
}
async Task Example2()
{
await DoSomethingBenignAsync().ConfigureAwait(false);
await DoSomethingUnexpectedAsync();
}
Take a look at Example2
. The first method we await
always runs asynchronously. By the time we hit the second await
, we know we're running on a thread pool thread, so there's no need for ConfigureAwait(false)
on the second call, right? Wrong. Despite having Async
in the name and returning a Task
, our second method wasn't written using async
and await
. Instead, it performs its own scheduling and uses a TaskCompletionSource
to communicate the result. When you resume from your await
, you might[1] end up running on whatever thread provided the result, which in this case is WPF's dispatcher thread. Whoops.
The key takeaway here is that you often don't know exactly what an 'awaitable' method does. With or without ConfigureAwait
, you might end up running somewhere unexpected. This can happen at any level of an async
call stack, so the surest way to avoid inadvertently taking ownership of a single-threaded context is to use ConfigureAwait(false)
with every await
, i.e., throughout the transitive closure.
Of course, there may be times when you want to resume on your current context, and that's fine. That is ostensibly why it's the default behavior. But if you don't genuinely need it, then I recommend using ConfigureAwait(false)
by default. This is especially true for library code. Library code can get called from anywhere, so it's best adhere to the principle of least surprise. That means not locking other threads out of your caller's context when you don't need it. Even if you use ConfigureAwait(false)
everywhere in your library code, your caller will still have the option to resume on their original context if that's what they want.
[1] This behavior may vary by framework and compiler version.
Async library best practice: ConfigureAwait(false) vs. setting the synchronization context
As @MrinalKamboj wrote in the comments, the approach of temporarily setting the SynchronizationContext to null at the public surface method and setting back has already been proposed here. There doesn't seem to be any specific problem associated with this (see Stephen Cleary's answer here).
Cascading ConfigureAwait(false), is it necessary?
Each method should make its own decision on whether to use ConfigureAwait(false)
. If that method needs its context (in this case, HttpContext.Curent
/ culture / identity), then it should not use ConfigureAwait(false)
.
Bear in mind that this code:
await MethodAsync().ConfigureAwait(false);
is roughly the same as this code:
var task = MethodAsync();
var configuredTask = task.ConfigureAwait(false);
await configuredTask;
In other words, MethodAsync
is called before ConfigureAwait
. So the method is called with the current context, and then the task it returned is awaited without capturing the context.
Usage of ConfigureAwait in .NET
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.
- 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.
- 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:
- I doubt you would notice any performance improvement.
- 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. - 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)
ConfigureAwait(false) and events in library code
You have to make a design call - is the event guaranteed to return on the same context that called the public method or not? Base your decision on similar use cases, such as WebClient
's DownloadDataCompleted
, an event that is raised when DownloadDataAsync
is called. Once you've decided, document that clearly and ensure that you obey that specification.
Generally speaking, I would choose the option to not invoke the event on the original context. If the caller wants to marshal back, it can always do that. But if it doesn't need to, you're forcing your entire async
chain to fight for the original contxt every time it returns from an async call, for nothing. Be context-free as much as you can, and marshal back at the last possible moment.
Of course, you can ask yourself if you need both an event and a Task-returning method that both signal the exact same async event. Is the client listening to the WebHookTriggered
event the same code that calls the public async method?
Why would I bother to use Task.ConfigureAwait(continueOnCapturedContext: false);
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 calledDownloadFileAsync
.DownloadFileAsync
also started in the UI context, but then stepped out of its context by callingConfigureAwait(false)
. The rest ofDownloadFileAsync
runs in the thread pool context. However, whenDownloadFileAsync
completes andDownloadFileButton
_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.
Related Topics
How to Pass Data (And References) Between Scenes in Unity
Pass List of Checkboxes into View and Pull Out Ienumerable
How to Enable Assembly Bind Failure Logging (Fusion) in .Net
Linq'S Distinct() on a Particular Property
Using .Net, How to Find the Mime Type of a File Based on the File Signature Not the Extension
How to Save Application Settings in a Windows Forms Application
Pass an Instantiated System.Type as a Type Parameter For a Generic Class
Collection Was Modified; Enumeration Operation May Not Execute
Is There a Constraint That Restricts My Generic Method to Numeric Types
Convert a String to an Enum in C#
How To: Execute Command Line in C#, Get Std Out Results
Windows Service Not Shown in Add Remove Programs Under Control Panel
The Calling Thread Cannot Access This Object Because a Different Thread Owns It
Programmatic Equivalent of Default(Type)