非等待线程在 api 返回后停止执行,这是一个已知的错误,还是任何人都可以解释发生了什么?



如果我有一个 api 控制器,并且我希望它尽快返回给用户,同时剥离一个线程来处理一些更长运行的进程,我想我不能像这样等待异步方法调用。

DoSomeNonAwaitedAsyncThing(data);
var result = await DoSomeNormalAsyncThing(data);
return result;

但是,当 api 控制器返回时,DoSomeNonAwaitedAsyncThing 代码将停止执行(没有异常或任何它停止运行的内容)。

如果我将 DoSomeNonAwaitedAsyncThing 的签名从异步任务更改为异步 void,那么线程将运行到完成,但它也会阻止 api 调用返回,直到非等待任务完成。

此外,如果我将调用方法的方式更改为 this(如下),那么该方法将运行完成,而不是在方法中途停止。

Task.Run(async () => { DoSomeNonAwaitedAsyncThing(data); }); 

我创建了一个带有项目的存储库来演示该问题。 https://github.com/JamesWebDev/AsyncThreadIssueExample

还值得注意的是,我将其添加到 Web 配置中,以便它应该允许在线程仍在运行时返回 api 调用。

<add key="aspnet:AllowAsyncDuringSyncStages" value="true"/>

DoSomeNonAwaitedAsyncThing代码将在api控制器返回时停止执行

问题很可能是因为DoSomeNonAwaitedAsyncThing正在尝试重新输入 ASP.NET Classic请求上下文。默认情况下,await将捕获当前上下文并恢复该上下文;在经典 ASP.NET 中,此"上下文"是 ASP.NET 经典请求上下文。但是,由于该请求上下文引用的请求已经完成,因此这可能会导致...并发症。

它与Task.Run一起使用的原因是,DoSomeNonAwaitedAsyncThing的当前上下文不再是 ASP.NET Classic 请求上下文;它现在是线程池上下文。当DoSomeNonAwaitedAsyncThing内部的await准备好继续时,线程池仍然存在。

作为最佳实践,应避免在 ASP.NET 上即发即弃(经典和核心都是如此)。正确的解决方案是让控制器写入可靠队列(例如 Azure 队列),并让独立的后台进程处理该操作(例如 Azure 函数)。独立后台进程有替代方案 - 经典进程为HostingEnvironment.QueueBackgroundWorkItemIRegisteredObject,核心进程为IHostedServiceIHostApplicationLifetime- 但最重要的是,控制器和后台进程之间至少需要一个可靠的队列。否则,您可能会在关机/回收期间丢失工作。

相关内容

最新更新