考虑以下示例:
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)
吗?还是这个例子是错误的,没有例外?
答案 0 :(得分:8)
除非BarB()
需要与UI对话,否则BarB()
所做的事情基本上无关紧要。这里的重要部分是该行中的await
:
await BarB();
await
捕获上下文(也是)-并且如果发现自己继续从继续协商将返回到正确的上下文错误的上下文。所以;除非您写:
await BarB().ConfigureAwait(false);
你应该在这里没事。
如果我们假设ConfigureAwait(false)
不需要直接更新UI或执行任何操作,则BarB()
中的BarB()
看起来很正常并且可能是正确的其他上下文相关操作。
答案 1 :(得分:2)
要补充Marc's answer-请记住async
是方法的实现细节。方法如何或为什么创建Task
并交还给您,这在很大程度上是透明的(这就是为什么async
不是签名的一部分,接口定义等不允许的原因)
虽然您的方法确实与它所调用的方法共享“当前上下文”,但这只是在这些方法(如果他们想知道当前上下文)将调用SynchronizationContext.Current
的范围内。然后,如有必要,他们将在该上下文上使用方法 来“恢复”。实际上,对上下文的任何“更改”通常都是通过将延续切换到已经具有正确上下文集的合适线程上来完成的。
这些方法通常不会通常会调用SetSynchronizationContext
。仅当方法确实调用时,您才需要担心对“您的”上下文的更改。 (如果某些基础结构代码的同步上下文是自由线程的,但需要其他环境资源,则可以调用该方法。它们将获得线程池线程,调用SetSynchronizationContext
,执行继续操作,然后再次取消设置同步上下文)。