基于包括 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)
是否足够,还是应该确保 Method1Async
和 Method2Async
也在内部使用 ConfigureAwait(false)
调用它们的异步方法?将它包含在所有这些中间方法中似乎很愚蠢,特别是如果
Method1Async
和 Method2Async
只是最终调用 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
如果您想忽略所有 TaskAwaiter
的 SynchronizationContext
,您必须为每个人使用 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/