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

2

5

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?

Tacnode answered 13/2, 2019 at 11:28 Comment(1)
"maybe the example is wrong and there's no exception?" - maybe you could compile and run the code and find out? Showing us an example and not even knowing whether it exhibits a behaviour you're wanting to ask about make for a bad question.Photochronograph
O
9

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.

Odelle answered 13/2, 2019 at 11:32 Comment(0)
P
2

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

Photochronograph answered 13/2, 2019 at 11:53 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.