Why Would I Bother to Use Task.Configureawait(Continueoncapturedcontext: False);

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.

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.

Why is writing ConfigureAwait(false) on every line with await always recommended and do I really need it?

As a general rule, yes. ConfigureAwait(false) should be used for every await unless the method needs its context.

I've seen that advice often here on Stack Overflow, and it's even what Stephen Cleary (a Microsoft MVP) says in his Async and Await article:

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

Stephen definitely knows his stuff, and I agree that the advice is technically accurate, but I've always thought that this is bad advice for two reasons:

  1. Beginners, and
  2. Maintenance risk

First, it's bad advice for beginners because synchronization context is a complex subject. If you start learning async/await by being told that "ConfigureAwait(false) should be used for every await unless the method needs its context", but you don't even know what "context" is and what it means to "need it", then you don't know when you shouldn't use it, so you end up always using it. That means you can run into bugs that will be very difficult to figure out unless you happen to learn that, yes, you did actually need that "context" thing and this magical "ConfigureAwait" thing made you lose it. You can lose hours trying to figure that out.

For applications of any kind, I believe the advice really should be the opposite: Don't use ConfigureAwait at all, unless you know what it does and you have determined that you absolutely don't need the context after that line.

However, determining you don't need the context can be either simple, or quite complex depending on what methods are called after. But even then - and this is the second reason I disagree with that advice - just because you don't need the context after that line right now, doesn't mean some code won't be added later that will use the context. You'll have to hope that whoever makes that change knows what ConfigureAwait(false) does, sees it, and removes it. Using ConfigureAwait(false) everywhere creates a maintenance risk.

This is what another Stephen, Stephen Toub (a Microsoft employee), recommends in the ConfigureAwait FAQ under the subheading "When should I use ConfigureAwait(false)?":

When writing applications, you generally want the default behavior (which is why it is the default behavior). ... This leads to the general guidance of: if you’re writing app-level code, do not use ConfigureAwait(false)

In my own application code, I don't bother trying to figure out where I can and can't use it. I just ignore that ConfigureAwait exists. Sure, there can be a performance improvement by using it where you can, but I really doubt that it will be a noticeable difference to any human, even if it is measurable by a timer. I don't believe the return on investment is positive.

The only exception to this is when you're writing libraries, as Stephen Toub points out in his article:

if you’re writing general-purpose library code, use ConfigureAwait(false)

That's for two reasons:

  1. A library has no idea about the context of the application it's being used in, so it can't use the context anyway, and
  2. If the person using the library decides to wait synchronously on your asynchronous library code, it could cause a deadlock that they cannot change because they can't change your code. (ideally, they shouldn't do that, but it can happen)

To address another point in your question: it's not always enough to use ConfigureAwait(false) on the first await and not the rest. Use it on every await in your library code. Stephen Toub's article under the heading "Is it ok to use ConfigureAwait(false) only on the first await in my method and not on the rest?" says, in part:

If the await task.ConfigureAwait(false) involves a task that’s already completed by the time it’s awaited (which is actually incredibly common), then the ConfigureAwait(false) will be meaningless, as the thread continues to execute code in the method after this and still in the same context that was there previously.

Edit: I finally got around to making this into an article on my site: Don’t use ConfigureAwait(false)

Correct use of ConfigureAwait(false) when chaining tasks

Ok so from the comments and chaining a few other questions together I think I have a better understanding of when to use configure await...

All of the below suggestions presume your task does not need to return to the calling thread. i.e. you have a valid ConfigureAwait use case

  1. ConfigureAwait configures the await, not the Task - You only need to use ConfigureAwait on the line where await is used

    From this answer: Using ConfigureAwait(false) on tasks passed in to Task.WhenAll() fails

  2. You do need to use ConfigureAwait even if it is the last line in the method

    From This answer: Does Task.ConfigureAwait(false) on the last method line affect anything?

  3. ConfigureAwait doesn't need to be called when using ContinueWith

    From this answer: ConfigureAwait(false) when using ContinueWith

  4. Tasks contained in Task.WhenAll are already configured with ConfigureAwait

    Also from this answer: Using ConfigureAwait(false) on tasks passed in to Task.WhenAll() fails

Which leaves me with my code looking like so:

var tasks = new List<Task>();

tasks.Add(DoWorkAsync(1));
tasks.Add(DoWorkAsync(2));
tasks.Add(DoWorkAsync(3));
tasks.Add(DoWorkAsync(4));
tasks.Add(DoWorkAsync(5));
tasks.Add(DoWorkAsync(6));

await Task.WhenAll(tasks.ToArray()).ConfigureAwait(false);

To my surprise it seems that using ConfigureAwait is advised by most of the community, although when talking about Threading/async/await this never really gets a mention.

Extra information: Why would I bother to use Task.ConfigureAwait(continueOnCapturedContext: false);

I'm not an expert in this field I've just linked together relevent other questions, so if I've missed anything / got anything wrong, feel free to chip in.

ConfigureAwait(false) on the single line

ConfigureAwait(false) has affect only on non completed awaitables (Task-like types) and only when under a SynchronizationContext.

The first simple example, if ConfigureAwait(false) is ommited:

private async Task test()
{
await Task.Run(() =>{});
}

If the test method is running under a SynchronizationContext, the continuation after the await will be posted to that SynchronizationContext.

Refer to the ConfigureAwait FAQ for a more detailed explanation.

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.

Thread synchronization with Task.ConfigureAwait(false)

Yes, you are correct. To avoid any thread synchronization issues you should apply appropriate locking around the line that is incrementing the count

ConfigureAwait: On which thread is the exception handled?

The exception will be on whatever thread the continuation would have happened on had there been no exception.

try
{
await ThrowingMethodAsync().ConfigureAwait(false);
}
catch (Exception e)
{
// Which thread am I on now?
//A: Likely a Thread pool thread unless ThrowingMethodAsync threw
// synchronously (without a await happening first) then it would be on the same
// thread that the function was called on.
}

try
{
await NonThrowingMethodAsync().ConfigureAwait(false);

// At this point we *may* be on a different thread

await ThrowingMethodAsync().ConfigureAwait(false);
}
catch (Exception e)
{
// Which thread am I on now?
//A: Likely a Thread pool thread unless ThrowingMethodAsync threw
// synchronously (without a await happening first) then it would be on the same
// thread that the function was called on.
}

For some more clarity:

private async Task ThrowingMethodAsync()
{
throw new Exception(); //This would cause the exception to be thrown and observed on
// the calling thread even if ConfigureAwait(false) was used.
// on the calling method.
}

private async Task ThrowingMethodAsync2()
{
await Task.Delay(1000);
throw new Exception(); //This would cause the exception to be thrown on the SynchronizationContext
// thread (UI) but observed on the thread determined by ConfigureAwait
// being true or false in the calling method.
}

private async Task ThrowingMethodAsync3()
{
await Task.Delay(1000).ConfigureAwait(false);
throw new Exception(); //This would cause the exception to be thrown on the threadpool
// thread but observed on the thread determined by ConfigureAwait
// being true or false in the calling method.
}

Why am I still on the Main Thread when I specified ConfigureAwait(false)?

It could well be that the task you're running is completing immediately - which means that the await doesn't even need to schedule a continuation. An async method only schedules a continuation when it needs to; if you await something which has already completed, it just keeps going - in the same thread.

ConfigureAwait is a hint that you don't need to continue in the same context; it's not a requirement that you mustn't continue on the same context.

If you change your task to something that doesn't complete immediately - e.g. call Thread.Sleep(1000) (which of course you wouldn't normally do) I'd expect you to see a change in behaviour.

It's possible that I've missed something else and that this isn't the cause of the problem, but it would at least be worth trying that first.



Related Topics



Leave a reply



Submit