问题 返回任务或等待和ConfigureAwait(false)


假设有一个像这样的方法的服务库

public async Task<Person> GetPersonAsync(Guid id) {
  return await GetFromDbAsync<Person>(id);
}

遵循最佳实践 的SynchronizationContext 更好用

public async Task<Person> GetPersonAsync(Guid id) {
  return await GetFromDbAsync<Person>(id).ConfigureAwait(false);
}

但是当你只有一个操作(我认为)最好直接返回任务。看到 在异步方法结束时,我应该返回还是等待?

public Task<Person> GetPersonAsync(Guid id) {
  return GetFromDbAsync<Person>(id);
}

在最后一种情况下,您无法使用 ConfigureAwait(假) 因为不等待该方法。

什么是最佳解决方案(以及为什么)?


13176
2018-05-08 08:42


起源

我认为最后一个(委托)是最清楚的,不涉及创建额外的状态机。除非你在做 别的 在依赖于异步调用结果的方法内部,我认为没有必要使用 await。 - Patryk Ćwiek
最后一个对我来说最有意义。它返回一个任务,您可以在任何调用GetPersonAsync的地方等待它 - Dennis_E
那么直接返回Task的解决方案不会捕获SynchronizationContext? - sevenmy
你正在做的是让你的来电者决定如何做 他们 我想等待 Task 完成 - 如果 那 代码是 async, 它 可以决定是否捕获上下文 await结果。 - Damien_The_Unbeliever
@Damien_The_Unbeliever好,因此在最新的例子中,没有切换上下文开销? - sevenmy


答案:


每个选项都有自己的细节,请检查 这个 和 这个。如果你理解它们,你可以决定什么是最适合你的。

因此,直接返回Task的解决方案无法捕获   的SynchronizationContext?

捕获当前同步上下文不是任务。它的 TaskAwaiter.OnCompleted (要么 ConfiguredTaskAwaitable.OnCompleted, 的情况下 ConfigureAwait),由C#编译器生成的代码间接调用,作为其中的一部分 await 任务说明。

所以,如果你不使用 await,你不应该担心 SynchronizationContext 捕获,它不会神奇地发生在自己身上。这可能使第3选项成为最有利的选项,但请记住它 异常传播行为


10
2018-05-08 10:17



谢谢。我认为这是一个非常特定领域的选择。如果调用的方法确实是异步的,那么有什么不同吗?例如,使用EF进行异步查询(错误处理很重要)。 - sevenmy
另一个好的阅读是: 异步性能:了解异步和等待的成本 - sevenmy
@sevenmy,An async Task 在你观察任务之前,方法永远不会抛出 await task, task.Wait(), 要么 task.Result。 OTOH,非同步 Task 方法可能会立即投掷。 然而,在您的客户端代码中,您不应该做任何假设。像它一样的代码可以同步(在同一堆栈帧上)或异步抛出。 - Noseratio