异步库最佳实践: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的回答 )。