When you have server-side code (i.e. some ApiController
) and your functions are asynchronous - so they return Task<SomeObject>
- is it considered best practice that any time you await functions that you call ConfigureAwait(false)
?
I had read that it is more performant since it doesn't have to switch thread contexts back to the original thread context. 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.
I've typed up an example of what I am talking about below:
public class CustomerController : ApiController
{
public async Task<Customer> Get(int id)
{
// you are on a particular thread here
var customer = await GetCustomerAsync(id).ConfigureAwait(false);
// now you are on a different thread! will that cause problems?
return customer;
}
}
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.
Brief answer to your question: No. You shouldn't call ConfigureAwait(false)
at the application level like that.
TL;DR version of the long answer: If you are writing a library where you don't know your consumer and don't need a synchronization context (which you shouldn't in a library I believe), you should always use ConfigureAwait(false)
. Otherwise, the consumers of your library may face deadlocks by consuming your asynchronous methods in a blocking fashion. This depends on the situation.
Here is a bit more detailed explanation on the importance of ConfigureAwait
method (a quote from my blog post):
When you are awaiting on a method with await keyword, compiler generates bunch of code in behalf of you. One of the purposes of this action is to handle synchronization with the UI (or main) thread. The key component of this feature is the SynchronizationContext.Current which gets the synchronization context for the current thread. SynchronizationContext.Current is populated depending on the environment you are in. The GetAwaiter method of Task looks up for SynchronizationContext.Current. If current synchronization context is not null, the continuation that gets passed to that awaiter will get posted back to that synchronization context. When consuming a method, which uses the new asynchronous language features, in a blocking fashion, you will end up with a deadlock if you have an available SynchronizationContext. When you are consuming such methods in a blocking fashion (waiting on the Task with Wait method or taking the result directly from the Result property of the Task), you will block the main thread at the same time. When eventually the Task completes inside that method in the threadpool, it is going to invoke the continuation to post back to the main thread because SynchronizationContext.Current is available and captured. But there is a problem here: the UI thread is blocked and you have a deadlock!
Also, here are two great articles for you which are exactly for your question:
The Perfect Recipe to Shoot Yourself in The Foot - Ending up with a Deadlock Using the C# 5.0 Asynchronous Language Features
Asynchronous .NET Client Libraries for Your HTTP API and Awareness of async/await's Bad Effects
Finally, there is a great short video from Lucian Wischik exactly on this topic: Async library methods should consider using Task.ConfigureAwait(false).
Hope this helps.
Task
walks the stack to get the SynchronizationContext
, which is wrong. The SynchronizationContext
is grabbed before the call to the Task
and then the rest of the code is continued on the SynchronizationContext
if SynchronizationContext.Current
is not null.
SynchronizationContext.Current
is clear / or that the library is called within a Task.Run()
instead of having to write .ConfigureAwait(false)
all over the class library?
.ConfigureAwait(false)
s. Perhaps it would be easier for library authors if that were the default behavior, but I would presume that making it a little bit harder to write a library correctly is better than making it a little bit harder to write an app correctly.
The biggest draw back I've found with using ConfigureAwait(false) is that the thread culture is reverted to the system default. If you've configured a culture e.g ...
<system.web>
<globalization culture="en-AU" uiCulture="en-AU" />
...
and you're hosting on a server whose culture is set to en-US, then you will find before ConfigureAwait(false) is called CultureInfo.CurrentCulture will return en-AU and after you will get en-US. i.e.
// CultureInfo.CurrentCulture ~ {en-AU}
await xxxx.ConfigureAwait(false);
// CultureInfo.CurrentCulture ~ {en-US}
If your application is doing anything which requires culture specific formatting of data, then you'll need to be mindful of this when using ConfigureAwait(false).
ConfigureAwait(false)
is used.
I have some general thoughts about the implementation of Task
:
Task is disposable yet we are not supposed to use using. ConfigureAwait was introduced in 4.5. Task was introduced in 4.0. .NET Threads always used to flow the context (see C# via CLR book) but in the default implementation of Task.ContinueWith they do not b/c it was realised context switch is expensive and it is turned off by default. The problem is a library developer should not care whether its clients need context flow or not hence it should not decide whether flow the context or not. [Added later] The fact that there is no authoritative answer and proper reference and we keep fighting on this means someone has not done their job right.
I have got a few posts on the subject but my take - in addition to Tugberk's nice answer - is that you should turn all APIs asynchronous and ideally flow the context . Since you are doing async, you can simply use continuations instead of waiting so no deadlock will be cause since no wait is done in the library and you keep the flowing so the context is preserved (such as HttpContext).
Problem is when a library exposes a synchronous API but uses another asynchronous API - hence you need to use Wait()
/Result
in your code.
Task.Dispose
if you want; you just don't need to the vast majority of the time. 2) Task
was introduced in .NET 4.0 as part of the TPL, which did not need ConfigureAwait
; when async
was added, they reused the existing Task
type instead of inventing a new Future
.
Task
s; the "context" controlled by ContinueWith
is a SynchronizationContext
or TaskScheduler
. These different contexts are explained in detail on Stephen Toub's blog.
Thread
s, but doesn't anymore with ContinueWith()
), this makes your answer confusing to read.
Success story sharing
HttpContext.Current
is flowed by the ASP.NETSynchronizationContext
, which is flowed by default when youawait
, but it's not flowed byContinueWith
. OTOH, the execution context (including security restrictions) is the context mentioned in CLR via C#, and it is flowed by bothContinueWith
andawait
(even if you useConfigureAwait(false)
).ConfigureAwait
actually only makes sense when you await tasks, whereasawait
acts on any "awaitable." Other options considered were: Should the default behavior discard context if in a library? Or have a compiler setting for the default context behavior? Both of these were rejected because it's harder to just read the code and tell what it does.ConfigureAwait(false)
to avoid aResult
/Wait
-based deadlock because on ASP.NET you should not be usingResult
/Wait
in the first place.