基于包括 this excellent one here 在内的大量书籍和博客,很明显,当编写一个暴露辅助异步方法(即包装方法)的 dll 库时,通常认为最好的做法是在线程池上内部完成实际异步方法的 I/O 任务像这样的线程(为了简洁起见,下面显示了伪代码,我以 HttpClient 为例)

public Async Task<HttpResponseMessage> MyMethodAsync(..)
{
    ...
    var httpClient = new HttpClient(..);
    var response = await httpClient.PostAsJsonAsync(..).ConfigureAwait(false);
    ...
    return response;
}

这里的关键是 ConfigureAwait(false) 的使用,以便 IO 任务完成发生在线程池线程而不是原始线程上下文中,从而潜在地防止死锁。

我的问题是从来电者的角度来看的。我对调用者和上述方法调用之间存在方法调用层的场景特别感兴趣,如下例所示。
CallerA -> Method1Async -> Method2Async -> finally the above MyMethodAsync

仅在 final方法上使用 ConfigureAwait(false) 是否足够,还是应该确保 Method1AsyncMethod2Async 也在内部使用 ConfigureAwait(false) 调用它们的异步方法?
将它包含在所有这些中间方法中似乎很愚蠢,特别是如果 Method1AsyncMethod2Async 只是最终调用 MyMethodAsync 的重载。
有什么想法,请赐教!

更新了示例
因此,如果我有一个具有以下私有(private)异步方法的库,
private async Task<string> MyPrivateMethodAsync(MyClass myClass)
{
    ...
    return await SomeObject.ReadAsStringAsync().ConfigureAwait(false);
}

我应该确保以下公共(public)重载方法也包括 ConfigureAwait(false) ,如下所示?
public async Task<string> MyMethodAsync(string from)
{
        return await MyPrivateMethodAsync(new (MyClass() { From = from, To = "someDefaultValue"}).ConfigureAwait(false);
}
public async Task<string> MyMethodAsync(string from, string to)
{
        return await MyPrivateMethodAsync(new (MyClass() { From = from, To = to }).ConfigureAwait(false);
}

最佳答案

当然不。 ConfigureAwait 顾名思义配置了 await 。它只影响与其耦合的 await
ConfigureAwait 实际上返回了一个不同的等待类型,ConfiguredTaskAwaitable 而不是 Task,它反过来返回一个不同的等待类型 ConfiguredTaskAwaiter 而不是 134145

如果您想忽略所有 TaskAwaiterSynchronizationContext,您必须为每个人使用 await

如果你想限制 ConfigureAwait(false) 的使用,你可以在最顶部使用我的 ConfigureAwait(false)(参见 here ):

async Task CallerA()
{
    using (NoSynchronizationContextScope.Enter())
    {
        await Method1Async();
    }
}

关于C# async/await 链接与 ConfigureAwait(false),我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/28776408/

10-13 05:27