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

本文关键字:false vs 设置 上下文 同步 ConfigureAwait 最佳 异步 | 更新日期: 2023-09-27 18:06:34

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

作为用ConfigureAwait(false)填充整个代码库的替代方法,可以简单地在public surface方法中将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,而在另一种情况下,我可能希望不使用SynchronizationContext运行相同的方法。当ConfigureAwait(false)嵌入到任何地方时,这是不可能的。当然,这是一个相当罕见的需求,但我在Npgsql工作时碰到了它(触发这个问题)。

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

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