ConfigureAwait(false)
在 C# 中使用 await/async时,有很多指导方针。
似乎一般建议是ConfigureAwait(false)
在库代码中使用,因为它很少依赖于同步上下文。
但是,假设我们正在编写一些非常通用的实用程序代码,它将函数作为输入。一个简单的例子可能是以下(不完整的)功能组合器,以使简单的基于任务的操作更容易:
地图:
public static async Task<TResult> Map<T, TResult>(this Task<T> task, Func<T, TResult> mapping)
{
return mapping(await task);
}
平面图:
public static async Task<TResult> FlatMap<T, TResult>(this Task<T> task, Func<T, Task<TResult>> mapping)
{
return await mapping(await task);
}
问题是,我们应该ConfigureAwait(false)
在这种情况下使用吗?我不确定上下文捕获是如何工作的。关闭。
一方面,如果以功能方式使用组合子,则不需要同步上下文。另一方面,人们可能会滥用 API,并在提供的函数中做与上下文相关的事情。
一种选择是为每个场景(Map
和MapWithContextCapture
或某些东西)使用单独的方法,但感觉很难看。
另一种选择可能是将选项添加到 map/flatmap from 和 into a ConfiguredTaskAwaitable<T>
,但由于 awaitables 不必实现接口,这将导致大量冗余代码,在我看来更糟。
是否有一种将责任切换给调用者的好方法,这样实现的库不需要对提供的映射函数中是否需要上下文做出任何假设?
或者仅仅是一个事实,即异步方法在没有各种假设的情况下组合得不太好?
编辑
只是为了澄清一些事情:
- 问题确实存在。当您在实用程序函数中执行“回调”时,添加
ConfigureAwait(false)
将导致空同步。语境。 - 主要问题是我们应该如何应对这种情况。我们是否应该忽略某人可能想要使用同步的事实。上下文,或者除了添加一些重载、标志等之外,是否有一种将责任转移给调用者的好方法?
正如一些答案所提到的,可以在该方法中添加一个布尔标志,但正如我所见,这也不是很漂亮,因为它必须通过 API 一路传播(因为有更多“实用”功能,取决于上面显示的功能)。