Consider this example:
async Task Foo()
{
button.Text = "This is the UI context!";
await BarA();
button.Text = "This is still the UI context!";
await BarB();
button.Text = "Oh no!"; // exception (?)
}
async Task BarA()
{
await Calculations();
}
async Task BarB()
{
await Calculations().ConfigureAwait(false);
}
How do I know if calling await BarB()
changes the context without reading the body of the async Task BarB()
function? Do I really need to know exactly whether an async function calls ConfigureAwait(false)
at any point? Or maybe the example is wrong and there's no exception?
The call to the async method starts an asynchronous task. However, because no Await operator is applied, the program continues without waiting for the task to complete. In most cases, that behavior isn't expected.
The async and await keywords don't cause additional threads to be created. Async methods don't require multithreading because an async method doesn't run on its own thread. The method runs on the current synchronization context and uses time on the thread only when the method is active.
No. It schedules the remaining code to run on the correct context.
The word “async” before a function means one simple thing: a function always returns a promise. Other values are wrapped in a resolved promise automatically. So, async ensures that the function returns a promise, and wraps non-promises in it.
What BarB()
does is largely irrelevant, unless BarB()
needs to talk to the UI. The important part here is the await
in the line:
await BarB();
it is that await
which captures context (too) - and negotiates getting back onto the correct context if it finds itself continuing from the wrong context. So; unless you write:
await BarB().ConfigureAwait(false);
you should be fine here.
The ConfigureAwait(false)
in BarB()
looks normal and is probably correct, if we assume that BarB()
doesn't need to directly update the UI or perform any other context-bound operation.
To supplement Marc's answer - bear in mind that async
is an implementation detail of a method. How or why a method creates a Task
that it hands back to you are largely transparent (and that's why async
isn't part of the signature, not allowed in interface definitions, etc)
Whilst it's true that your method shares it's "current context" with methods it calls, that's only to the extent that those methods, if they want to know the current context, will call SynchronizationContext.Current
. They'll then use methods on that context to get "back on it" if necessary. Any "changes" to the context will usually actually be performed by switching continuations onto a suitable thread that already has the right context set.
What these methods won't normally do is call SetSynchronizationContext
. It's only if a method does call that that you have to worry about changes to "your" context. (Some infrastructure code may call that method if their synchronization context is free-threaded but requires other ambient resources. They'll get a thread pool thread, call SetSynchronizationContext
, execute the continuation and then unset the synchronization context again).
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