When you await
a Task
, the continuation by default runs on the same thread. The only time you ever actually need this is if you're on the UI thread, and the continuation needs to run on the UI thread as well.
You can control this by using ConfigureAwait
, e.g.:
await SomeMethodAsync().ConfigureAwait(false);
...which can be useful to offload work from the UI thread that doesn't need to run there. (But see Stephen Cleary's comment below.)
Now consider this bit of code:
try
{
await ThrowingMethodAsync().ConfigureAwait(false);
}
catch (Exception e)
{
// Which thread am I on now?
}
And how about this?
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?
}
ConfigureAwait(continueOnCapturedContext: false) is used to avoid forcing the callback to be invoked on the original context or scheduler. This has a few benefits: Improving performance.
In 99% of the cases, you should use ConfigureAwait(false). In . NET Framework by default the Task execution will continue on the captured context, this is ConfigureAwait(true).
By default, when you use async/await, it will resume on the original thread that started the request. However, if another long-running process currently has taken over that thread, you will be stuck waiting for it to complete. To avoid this issue, you can use a method called ConfigureAwait with a false parameter.
As a general rule, every piece of code that is not in a view model and/or that does not need to go back on the main thread should use ConfigureAwait false. This is simple, easy and can improve the performance of an application by freeing the UI thread for a little longer.
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.
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With