Best Practice to Call Configureawait For All Server-Side Code

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 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 con­text), 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.


  1. 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.


  1. 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:

  1. I doubt you would notice any performance improvement.
  2. 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.
  3. 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 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.



Related Topics



Leave a reply



Submit