【问题标题】:async void vs async task in Web API: an asynchronous operation was still pendingWeb API 中的 async void 与异步任务:异步操作仍在等待中
【发布时间】:2016-12-06 16:53:14
【问题描述】:

我们有如下所示的 Web API 控制器:

public class HomeController : ApiController
{
    Logger logger = new Logger();

    [HttpGet, Route("")]
    public IHttpActionResult Index()
    {
        logger.Info("Some info logging");

        return Ok();
    }
}

在内部,我们的记录器使用 HttpClientPOST 数据到 Azure 事件中心。这意味着记录器有一个内部调用异步方法的同步方法。

损坏的实现

当我们的记录器有一个带有async void 签名的私有方法时:

public class Logger
{
    public void Info(string message)
    {
        LogAsync(message);
    }

    private async void LogAsync(string message)
    {
        await PostAsync(message);
    }
}

我们看到以下错误:

工作实施

如果我们将记录器更改为使用这样的async Task 签名,它可以工作:

public class Logger
{
    public void Info(string message)
    {
        LogAsync(message);
    }

    private async Task LogAsync(string message)
    {
        await PostAsync(message);
    }
}

问题

在这两种情况下,我们的日志消息都会发送到事件中心。我想知道为什么async Task 允许控制器返回预期结果,而async void 导致InvalidOperationException

另外,有没有关于从同步方法调用异步代码的最佳实践?

我在这里发布了一个示例解决方案: https://github.com/kevinkuszyk/async-task-vs-async-void-sample

【问题讨论】:

  • 为什么你必须首先从同步方法中调用它?如果底层操作是异步的,则进行调用它的操作async。这就是async 存在的原因。
  • 这两个实现都被破坏了。一个告诉你它坏了,所以你可以修复它,另一个只是不工作,没有告诉你它不工作,所以你没有意识到它不工作。
  • @MichaelCoxon 返回一个你刚刚扔到地板上的任务并没有更好的。它仅在消费者实际使用它来确定操作何时完成时才有用。
  • @KevinKuszyk:"It needs to be synchronous so that consuming code can log and move on." - 我并不完全遵循那里的推理。如果操作宣传自己是同步的,那么消费代码将期望等待它完成。试图隐藏异步操作听起来像是在尝试一种“一劳永逸”的方法。但是该方法的“忘记”部分意味着您不在乎操作是否成功。那么为什么首先要处理错误呢?

标签: c# asp.net-web-api async-await


【解决方案1】:

避免对这种方法的优点进行判断,您实际问题的答案在this question的答案中。

在保持这种使用模式的同时,您可以通过多种方式修复它:

  • 不要在awaitLogAsync()
  • 使用Task.Run(() => LogAsync());

【讨论】:

  • 谢谢@sellotape - 您链接到的问题提供了我所缺少的背景信息。上面的示例是对重现我们遇到的问题的简化。在我们的实际实现中,记录器写入TraceSource,并且我们在另一端插入了一个侦听器,它将消息发送到事件中心。 TraceListener 上的合约意味着我们的入口点是同步方法。正如我上面提到的,我们使用 HttpClient 异步将消息发送到 Azure,因此出现了问题。我也很想知道你对这种方法的看法。
【解决方案2】:

如果你调用一个async方法同步调用它如下:

async Task MyMethod()
{
   ...
}

public void WaitOnTask()
{
  var resultTask = MyMethod();
  resultTask.GetAwaiter().GetResult();
}

async void 应该避免,因为没有什么可以阻止方法立即返回,当调用者不希望方法在异步过程完成的情况下返回时,这会导致问题,就像 ASP.NET 运行时的情况一样。

【讨论】:

    猜你喜欢
    • 2014-09-06
    • 2017-03-23
    • 2023-03-23
    • 2014-03-18
    • 1970-01-01
    • 1970-01-01
    • 2016-07-24
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多