Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How do I know if an async method call changes the caller's context?

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?

like image 877
matt-pielat Avatar asked Feb 13 '19 11:02

matt-pielat


People also ask

What happens when you call an async method?

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.

Does async call create a new thread?

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.

Does await cause context switch?

No. It schedules the remaining code to run on the correct context.

Does an async function always return a promise?

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.


2 Answers

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.

like image 134
Marc Gravell Avatar answered Sep 21 '22 04:09

Marc Gravell


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

like image 26
Damien_The_Unbeliever Avatar answered Sep 22 '22 04:09

Damien_The_Unbeliever