首页 > 解决方案 > 我应该将 Task.Run() 用于执行 I/O 的 BackgroundService

问题描述

我有一个需要执行一些后台任务的 ASP.NET Core 5 API。这些后台任务将在整个应用程序生命周期中运行,并且将读取 azure 队列,因此花费大量时间无所事事。

我已经读过 I/O 你不应该使用 Task.Run() 因为它效率低下,但如果在我的情况下我没有等待 I/O 调用,我正在等待发生的后台任务做很多 I/O 调用,可以吗?似乎如果我不使用 Task.Run() ,那么每次我的后台任务从队列中获取一些东西,所以等待之后的代码继续,那么它将阻塞 ASP 中的一些主线程,这些主线程在第一个后台处理任务地方。

标签: c#asp.netasp.net-coreasynchronous

解决方案


这些后台任务将在整个应用程序生命周期中运行,并且将读取 azure 队列,因此花费大量时间无所事事。

我同意其他人的评论:到目前为止,最好的解决方案是将这些完全移出 ASP.NET 进程。例如,Azure Functions 具有对 Azure 存储队列的内置支持。Azure Functions 在进程内通过 ASP.NET 的优势包括:

  • 您的 API (ASP.NET) 可以独立于后端 (Azure Function) 进行扩展。
  • 后端缩放是自动的。随心所欲地上升,可能下降到零。
  • 后端进程有自己的线程池,因此不会干扰 ASP.NET 线程池。

我已经读过 I/O 你不应该使用 Task.Run() 因为它效率低下

Task.Run应该避免在 ASP.NET 的请求处理管道中。无论正在完成的工作类型如何,它总是效率低下。

Task.Run 对于 ASP.NET 进程中的后台任务可能很有用,尤其是在后台任务启动期间完成了阻塞工作的情况下。

ASP.NET的主要内容Task.Run您不想干扰 ASP.NET 线程池启发式。定期将工作排队到线程池是一个问题,因为您正在窃取线程然后定期重新注入它们。因此,Task.Run在处理每个 HTTP 请求或处理每个队列项时使用将是一个坏主意。在启动时完成一次Task.Run并不重要。

似乎如果我不使用 Task.Run() 那么每次我的后台任务从队列中获取一些东西,所以等待之后的代码继续,那么它将阻塞 ASP 中的一些主线程,这些主线程在第一个后台处理任务地方。

如果在您的消息处理期间正在进行阻塞工作,这是一个有效的问题。

考虑async/await的一种方法是,每种方法都被分解成片段(在每个await点),并且每个片段都单独安排,一次一个。每次调度一个片段时,它会占用一个线程池线程,运行该片段,然后返回该线程。只要工作速度快,这很好,但如果其中一个有阻塞工作,那么 ASP.NET 线程池启发式可能会受到影响。


推荐阅读