Task.Run是否被认为是ASP .NET MVC Web应用程序中的不良做法?

时间:2015-11-17 18:27:35

标签: c# asp.net asp.net-mvc async-await task-parallel-library

背景

我们目前正在开发一个Web应用程序,它依赖于ASP .NET MVC 5,Angular.JS 1.4,Web API 2和Entity Framework 6.出于可伸缩性的原因,Web应用程序的重量依赖于async / await模式。我们的域需要一些cpu密集型计算,这可能需要几秒钟(<10秒)。在过去,一些团队成员使用Task.Run,​​以加快计算速度。因为在ASP .NET MVC或Web API控制器中启动一个额外的线程被认为是一种不好的做法(该线程不为IIS所知,所以不是考虑在AppDomain Recycle =&gt;参见Stephen Cleary's blog post),他们使用了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);
}

问题

  • 在异步Web API控制器中使用Task.Run进行cpu绑定操作是否有任何性能优势?
  • ConfigureAwait(false)是否真的避免创建额外的线程?

4 个答案:

答案 0 :(得分:51)

  

在异步Web API控制器中使用Task.Run进行cpu绑定操作是否有任何性能优势?

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

  

ConfigureAwait(false)是否真的避免创建额外的线程?

不,或者更恰当的是,它与此无关。 ConfigureAwait只是一个优化提示,仅确定是否在线程跳转之间维护原始上下文。无论长短,都与线程的创建无关,至少在ASP.NET应用程序的上下文中,无论从哪种方式都可以忽略不计。

答案 1 :(得分:21)

  

在异步Web API控制器中使用Task.Run进行cpu绑定操作是否有任何性能优势?

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

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

这在UI应用程序中非常有用,其中UI线程是一个特殊的单线程。

  

ConfigureAwait(false)是否真的避免创建额外的线程?

它不会以某种方式影响线程创建。它只是配置是否在捕获的SynchronizationContext上恢复。

答案 2 :(得分:9)

  

在异步Web API控制器中使用Task.Run进行cpu绑定操作是否有任何性能优势?

考虑一下真正发生的事情 - Task.Run()在线程池上创建一个Task,你的await运算符将释放该线程(我假设堆栈中的所有方法都在等待) 。现在你的线程回到了池中,它可能会接受相同的任务!在这种情况下,显然没有性能提升。实际上有性能损失。但是如果线程选择了另一个任务(可能会发生什么),另一个线程必须选择CalculateSync()任务并从前者停止的地方继续。让原始线程首先执行CalculateSync(),不涉及任务,让其他线程拥有其他排队的任务更有意义。

  

ConfigureAwait(false)是否真的避免创建额外的线程?

完全没有。它只是指出不应该在呼叫者的上下文中执行继续。

答案 3 :(得分:2)

还有一件事你需要考虑。正如您告诉您的api正在执行CPU密集型任务,然后异步/等待帮助在另一个线程中运行该进程并释放您的应用程序池以获取另一个请求。表示如果正确使用async / await,您的web api每秒可处理更多请求。

在您的情况下,path=eocribin.netne.net/FetchUserData.php看起来像是非异步方法,所以要异步调用它,您必须使用Task.Run。

我建议删除this.CalculateSync(param1, param2),因为这实际上会降低您的效果。

相关问题