我如何知道异步方法调用是否更改了调用者的上下文?

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

考虑这个例子:

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);
}

我如何知道调用 await BarB() 是否更改了上下文 而无需 读取 async Task BarB() 函数的主体?我真的需要确切地知道异步函数是否在任何时候调用 ConfigureAwait(false) 吗?又或者例子是错误的,没有例外?

BarB() 做什么在很大程度上是无关紧要的,除非 BarB() 需要与 UI 交谈。这里的重要部分是行中的 await

await BarB();

await 也捕获上下文 - 并协商将 返回 到正确的上下文,如果它发现自己从 错误的 上下文继续。所以;除非你写:

await BarB().ConfigureAwait(false);

你在这里应该没问题。

BarB()中的ConfigureAwait(false)看起来很正常,可能是正确的,如果我们假设BarB()不需要直接更新UI 或执行任何其他上下文绑定操作。

补充 - 请记住 async 是方法的 实现细节 。一个方法如何或为什么创建它返回给你的 Task 在很大程度上是透明的(这就是为什么 async 不是签名的一部分,在接口定义等中不允许)

虽然您的方法确实与其调用的方法共享 "current context",但这只是在这些方法(如果它们想知道当前上下文)将调用 SynchronizationContext.Current 的范围内。然后,如果需要,他们将使用方法 on 该上下文来获取 "back on it"。对上下文的任何 "changes" 通常实际上是通过 将延续切换到已经设置了正确上下文的合适线程来执行的 .

这些方法通常不会调用SetSynchronizationContext。只有当一个方法确实调用它时,您才需要担心 "your" 上下文的更改。 (如果它们的同步上下文是自由线程但需要其他环境资源,一些基础结构代码可能会调用该方法。它们将获得一个线程池线程,调用 SetSynchronizationContext,执行延续,然后再次取消设置同步上下文)。