是任务.在ASP.NET MVC Web应用程序中运行被认为是错误的做法

本文关键字:认为是 运行 错误 应用程序 任务 ASP NET Web MVC | 更新日期: 2023-09-27 18:22:13

背景

我们目前正在开发一个web应用程序,该应用程序依赖于ASP.NETMVC5、Angular.JS1.4、Webneneneba API 2和EntityFramework6。出于可伸缩性的原因,web应用程序的繁重性依赖于异步/等待模式。我们的域需要一些cpu密集型计算,这可能需要几秒钟(<10s)。过去,一些团队成员使用Task.Run来加快计算速度。由于在ASP.NETMVC或Webneneneba API控制器中启动一个额外的线程被认为是一种糟糕的做法(IIS不知道该线程,因此AppDomainRecycle=>请参阅StephenCleary的博客文章),他们使用了ConfigureAwait(false)。

示例

public async Task CalculateAsync(double param1, double param2)
{
    // CalculateSync is synchronous and cpu-intensive (<10s)
    await Task.Run(() => this.CalculateSync(param1, param2))).ConfigureAwait(false);
}

问题

  • 对于cpu-bound操作,在异步Web API控制器中使用Task.Run是否有任何性能优势
  • ConfigureAwait(false)真的可以避免创建额外的线程吗

是任务.在ASP.NET MVC Web应用程序中运行被认为是错误的做法

对于cpu-bound操作,在异步Web API控制器中使用Task.Run是否有任何性能优势?

零。没有一个事实上,您通过生成一个新线程来阻碍性能。在web应用程序的上下文中,生成线程与在"后台"运行不同。这是由于web请求的性质。当有传入请求时,会从池中获取一个线程来为该请求提供服务。使用async允许线程在请求结束前返回,如果则返回,并且仅当线程处于等待状态(即空闲)时返回。产生一个线程来进行工作,有效地闲置了主线程,允许它返回到池中,但仍然有一个活动线程。将原始线程返回到池中在这一点上没有任何作用。然后,当新线程完成其工作时,您必须从池中请求一个主线程,并最终返回响应。在所有工作完成之前,无法返回响应,因此无论您使用1个线程还是100个线程,异步还是同步,在所有工作完成之前都无法返回响应。因此,使用额外的线程只会增加开销。

ConfigureAwait(false)真的可以避免创建额外的线程吗?

不,或者更恰当地说,这与此无关。ConfigureAwait只是一个优化提示,并且仅确定在线程跳转之间是否保持原始上下文。总之,它与线程的创建无关,至少在ASP.NET应用程序的上下文中,无论哪种方式对性能的影响都可以忽略不计。

在用于cpu-bound操作的异步Web API控制器中使用Task.Run是否有任何性能优势?

没有。它是否受CPU限制并不重要

Task.Run将工作卸载到ThreadPool线程。web api请求已经使用了一个ThreadPool线程,所以您只是通过无理由地卸载到另一个线程来限制可伸缩性。

这在UI应用程序中很有用,因为UI线程是一个特殊的单线程。

ConfigureAwait(false)真的可以避免创建额外的线程吗?

它不会以这样或那样的方式影响线程的创建。它所做的只是配置是否在捕获的SynchronizationContext上恢复。

对于cpu-bound操作,在异步Web API控制器中使用Task.Run是否有任何性能优势?

想想到底发生了什么——Task.Run()在线程池上创建了一个Task,await操作符将释放线程(我假设堆栈中的所有方法也在等待)。现在,您的线程回到了池中,它可能会获得相同的Task!在这种情况下,显然没有性能提升。事实上,这是一个性能上的打击。但是,如果线程选择了另一个Task(这可能会发生),那么另一个线程将不得不选择CalculateSync() Task,并从前者停止的地方继续。如果让原始线程首先执行CalculateSync(),而不涉及Task,并让其他线程拥有其他排队的Task,则会更有意义。

ConfigureAwait(false)真的可以避免创建额外的线程吗?

一点也不。它只是指出不应该在调用方的上下文上执行延续。

还有一件事需要考虑。正如您所说,您的api正在执行CPU密集型任务,然后async/await帮助在另一个线程中运行进程,并为另一个请求释放应用程序池。这意味着如果你正确使用async/await,你的web api每秒可以处理更多的请求。

在您的情况下,看起来this.CalculateSync(param1, param2)是非异步方法,所以要异步调用它,您必须使用Task.Run.

我建议删除.ConfigureAwait(false),因为这实际上会降低您的性能。