【问题标题】:Return Task or await and ConfigureAwait(false)Return Task 或 await 和 ConfigureAwait(false)
【发布时间】:2026-02-12 00:10:01
【问题描述】:

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

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);
}

但是当你只有一个操作时(我认为)最好直接返回任务。见At the end of an async method, should I return or await?

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

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

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

【问题讨论】:

  • 我认为最后一个(委托)是最清楚的,不涉及创建额外的状态机。除非您在依赖于异步调用结果的方法中执行其他操作,否则我认为使用await 毫无意义。
  • 最后一个对我来说最有意义。它返回一个任务,您可以从调用 GetPersonAsync 的任何位置等待它
  • 那么直接返回Task的方案没有捕获到SynchronizationContext?
  • 你正在做的是让你的调用者决定他们希望如何等待Task完成 - 如果那个代码是async可以决定在awaiting结果时是否捕获上下文。
  • @Damien_The_Unbeliever 好的,因此在最新示例中没有切换上下文开销?

标签: c# async-await


【解决方案1】:

每个选项都有自己的细节,请查看thisthis。如果您了解它们,您可以决定什么是最适合您的。

所以直接返回任务的解决方案不会捕获 同步上下文?

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

所以,如果你不使用await,你不应该担心SynchronizationContext 捕获,它不会神奇地自行发生。这可能使第 3 个选项成为最有利的选项,但请记住它的 exception propagation behavior

【讨论】:

  • 谢谢。我认为这是一个非常特定于领域的选择。如果调用的方法真的是异步的,有什么区别吗?例如使用 EF 的异步查询(错误处理很重要)。
  • @sevenmy,在您使用await tasktask.Wait()task.Result 观察任务之前,async Task 方法永远不会抛出。 OTOH,非异步 Task 方法可能会立即抛出。 但是,在您的客户端代码中,您不应该做任何假设。总是这样的代码可以同步(在同一个堆栈帧上)或异步抛出。
最近更新 更多