【问题标题】:Is it ok to not await Tasks in Web API Controller可以不等待 Web API 控制器中的任务吗
【发布时间】:2018-03-15 15:11:42
【问题描述】:

所以我遇到了一些让我感到不舒服的代码,但我找不到确定的答案来确定它是否真的有问题。

我们有一个主要由消息总线使用的 ASP.Net Web API。需要为多个帐户启动一个平衡过程。平衡服务方法是异步的,并返回一个任务。代码是这样调用的:

foreach (AccountingGroup accountingGroup in Groups)
{
    ledgerService.CreateItemsAsync(accountingGroup.GLAccountingHeaderId);
}
return StatusCode(HttpStatusCode.NoContent);

这让我觉得在很多层面上都是错误的。我明白了意图。 “我们希望在所有这些组上运行此方法,但我们不需要等待它们完成。

显然没有使用 CancellationToken。如果整个过程运行时间过长,他们就依赖 AWS 来终止整个过程,无论如何,这不是我现在真正可以进行的重构。

我已经离开 C# 一年半,使用异步代码 2.5 年了,我觉得我在某个时候知道这里的问题,但我再也找不到它了。

处理这个问题的正确方法是什么?有问题吗?

【问题讨论】:

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


【解决方案1】:

不,这不行,后台工作运行时服务器可能会关闭应用程序域。处理这个问题的最好方法是使用像https://www.hangfire.io/ 这样的后台工作库。

如果您觉得工作将在下一分钟左右完成,您可以使用短期系统 HostingEnvironment.QueueBackgroundWorkItem(Func<CancellationToken,Task>) 但我不确定这是否适用于 ASP.NET Core,它旨在与以前版本的 ASP.NET。

编辑:找到参考,QueueBackgroundWorkItem 确实在 ASP.NET Core 中不起作用,但有 a similar way to handle these situations there

【讨论】:

  • 很有趣,但如果您表示结果正常,但如果它仍在运行并且您甚至不知道它会正常,则很危险。
  • @PatrickHofman 我同意,但作为一名顾问,我认为对于最小化现有项目的结构变化也有一些话要说。核心问题是它真的不应该这样做。但我确实喜欢有一个可用的解决方案来修复错误,而无需进行重大的结构更改。所以谢谢,斯科特。
  • @Riplikash 这就是重点:它不能解决问题。它隐藏了错误。你不希望这种情况发生。把数据库服务器拿出来,看看你的代码在实际失败时如何返回成功。
  • Patrick 的观点是为什么我首先推荐 hangfire,如果您的后台工作失败,它将重试指定的次数,然后将失败记录在管理员可以找到的地方。 QueueBackgroundWorkItemIApplicationLifetime 不会给你开箱即用的好处。
  • Scott,感谢您提供有关在 QueueBackgroundWorkItems、IApplicationLifetime 和 Hangfire 之间进行选择的更多信息。很高兴知道推荐的来源。 @PatrickHofman 我不反对,但正如我所说,在咨询情况下,当您无法实施正确的解决方案时,我认为寻找更好的方法来处理问题是有价值的。您不能总是更改应用程序的接口,因此即使是部分解决方案也很有价值,不需要您更改应用程序接口和工作流程。
【解决方案2】:

这还有问题吗?

是的,区别在于不想等待,实际上能够处理异常。

例如,如果您的代码因任何原因失败,您现在返回 HTTP 204,即成功状态。如果您等待结果,但它失败了,您很可能会收到 HTTP 500。

处理这个问题的正确方法是什么?

您应该等待结果,例如聚合任务并在它们上调用Task.WhenAll,这样您就不必分别等待它们中的每一个。

【讨论】:

  • 是的。假装动作已经完成,而实际上它还没有完成,这似乎是个坏主意。
【解决方案3】:

正确的方法是将你的 API 方法定义为 async,然后等待所有 async 方法完成:

public Task<IHttpActionResult> DoStuff()
{
    await Task.WhenAll(groups.Select(g =>
                     ledgerService.CreateItemsAsync(g.GLAccountingHeaderId));
    return StatusCode(HttpStatusCode.NoContent);
}

Patrick 的回答解释了“为什么”。向客户假装一个动作已经完成,而实际上它还没有完成,这似乎是个坏主意。

如果您想在后台运行这些东西,您可能会考虑使用 RabbitMq 等消息队列,并开发一种确保这些任务完成的故障安全方法。当事情失败时的反馈是好的。使用您当前的方法,您绝对无法确定此代码是否失败,这意味着如果它停止工作,您将不会意识到,直到它影响到其他东西。

【讨论】:

    【解决方案4】:

    您可以使用 QueueBackgroundWorkItem

    请看Getting QueueBackgroundWorkItem to complete if the web page is closed

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2017-12-27
      • 2020-01-07
      • 1970-01-01
      • 2021-09-19
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多