异步库最佳实践:ConfigureAwait(false)与设置同步上下文

众所周知,在通用库中,应在每次await调用时使用ConfigureAwait(false)以避免继续当前的SynchronizationContext。

作为使用ConfigureAwait(false)来编写整个代码库的替代方法,可以在公共表面方法中将SynchronizationContext设置为null一次,并在返回给用户之前将其恢复。 换一种说法:

 public async Task SomeSurfaceMethod() { var callerSyncCtx = SynchronizationContext.Current; SynchronizationContext.SetSynchronizationContext(null); try { // Do work } finally { SynchronizationContext.SetSynchronizationContext(callerSyncCtx); } } 

这也可以包含在using以获得更好的可读性。

这种方法是否有缺点,它最终不会产生相同的效果吗?

主要优点显然是可读性 – 删除所有ConfigureAwait(false)调用。 它还可以降低在某处忘记ConfigureAwait(false)的可能性(虽然分析器可以减轻这种情况,但可以说开发人员也可以忘记更改SynchronizationContext)。

一个有点奇特的优点是没有嵌入在所有方法中捕获SynchronizationContext的选择。 换句话说,在一种情况下,我可能想要使用SynchronizationContext运行方法X,而在另一种情况下,我可能希望在没有一个的情况下运行相同的方法。 当ConfigureAwait(false)嵌入到无法实现的任何地方时。 当然,这是一个非常罕见的要求,但我在处理Npgsql时遇到了这个问题(触发了这个问题)。

正如@MrinalKamboj在评论中所写, 这里已经提出了在公共表面方法中临时将SynchronizationContext设置为null并设置回来的方法。 似乎没有任何与此相关的具体问题(请参阅Stephen Cleary的回答 )。