Task.Run 是否被认为是 ASP .NET MVC Web 应用程序中的不良做法?
Is Task.Run considered bad practice in an ASP .NET MVC Web Application?
背景
我们目前正在开发一个 Web 应用程序,它依赖于 ASP .NET MVC 5、Angular.JS 1.4、Web API 2 和 Entity Framework 6。为了可扩展性原因,Web 应用程序严重依赖于 async/await 模式。我们的域需要一些 cpu 密集型计算,这可能需要几秒钟(<10 秒)。过去一些团队成员使用 Task.Run,为了加快 calculations.Since 在 ASP .NET MVC 或 Web API 控制器中启动额外的线程被认为是一种不好的做法(IIS 不知道该线程,因此在 AppDomain Recycle 上不考虑 => 请参阅 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) 真的避免创建额外的线程吗?
Is there any performance benefit in using Task.Run in an async Web API Controller for cpu-bound operations?
想想真正发生的事情 - Task.Run()
在线程池上创建一个任务,你的 await
操作员将释放线程(我假设堆栈中的所有方法也在等待) .现在您的线程已回到池中,它可能会执行相同的任务!在这种情况下,显然没有性能提升。实际上,性能受到了影响。但是如果线程选择另一个任务(这可能会发生),另一个线程将不得不选择 CalculateSync()
任务并从前一个停止的地方继续。让原始线程首先执行 CalculateSync()
更有意义,不涉及任何任务,让另一个线程拥有其他排队的任务。
Does ConfigureAwait(false) really avoid the creation of an extra thread?
完全没有。它只是指出不应在调用者的上下文中执行延续。
Is there any performance benefit in using Task.Run in an async Web API Controller for cpu-bound operations?
零。 None。事实上,您正在通过生成新线程来阻碍性能。在 Web 应用程序的上下文中,生成线程与 "background" 中的 运行 不同。这是由于网络请求的性质。当有传入请求时,会从池中取出一个线程来为请求提供服务。使用异步允许线程在请求结束前 returned,if 并且仅当线程处于等待状态时,即空闲。产生一个线程来处理,有效地空闲主线程,允许它 returned 到池中,但你仍然有一个活动线程。将原始线程返回到池中此时没有任何作用。然后,当新线程完成其工作时,您必须请求从池中返回一个主线程,最后 return 响应。在 所有 工作完成之前,无法 returned 响应,因此无论您使用 1 个线程还是一百个线程,异步或同步,都无法 returned 响应直到一切结束。因此,使用额外的线程只会增加开销。
Does ConfigureAwait(false) really avoid the creation of an extra thread?
不,或者更恰当地说,不是那个。 ConfigureAwait
只是一个优化提示,只判断线程跳转之间是否保持原来的上下文。总之,它与线程的创建无关,至少在 ASP.NET 应用程序的上下文中,无论哪种方式对性能的影响都可以忽略不计。
Is there any performance benefit in using Task.Run
in an async Web API Controller for cpu-bound operations?
没有。 CPU绑定与否并不重要。
Task.Run
将工作卸载到 ThreadPool
线程。 Web api 请求已经使用了一个 ThreadPool
线程,因此您只是通过无缘无故地卸载到另一个线程来限制可伸缩性。
这在 UI 应用程序中很有用,其中 UI 线程是一个特殊的单线程。
Does ConfigureAwait(false) really avoid the creation of an extra thread?
它不会以某种方式影响线程创建。它所做的只是配置是否在捕获的 SynchronizationContext
上恢复。
您还需要考虑一件事。正如您所说,您的 api 正在执行 CPU 密集型任务,然后 async/await 帮助 运行 另一个线程中的进程并释放您的应用程序池以用于另一个请求。意味着如果您正确使用 async/await,您的网站 api 每秒可以处理更多的请求。
在你的例子中 this.CalculateSync(param1, param2)
是非异步方法所以要异步调用它你必须使用 Task.Run.
我建议删除 .ConfigureAwait(false)
,因为这实际上会降低您的表现。
背景
我们目前正在开发一个 Web 应用程序,它依赖于 ASP .NET MVC 5、Angular.JS 1.4、Web API 2 和 Entity Framework 6。为了可扩展性原因,Web 应用程序严重依赖于 async/await 模式。我们的域需要一些 cpu 密集型计算,这可能需要几秒钟(<10 秒)。过去一些团队成员使用 Task.Run,为了加快 calculations.Since 在 ASP .NET MVC 或 Web API 控制器中启动额外的线程被认为是一种不好的做法(IIS 不知道该线程,因此在 AppDomain Recycle 上不考虑 => 请参阅 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) 真的避免创建额外的线程吗?
Is there any performance benefit in using Task.Run in an async Web API Controller for cpu-bound operations?
想想真正发生的事情 - Task.Run()
在线程池上创建一个任务,你的 await
操作员将释放线程(我假设堆栈中的所有方法也在等待) .现在您的线程已回到池中,它可能会执行相同的任务!在这种情况下,显然没有性能提升。实际上,性能受到了影响。但是如果线程选择另一个任务(这可能会发生),另一个线程将不得不选择 CalculateSync()
任务并从前一个停止的地方继续。让原始线程首先执行 CalculateSync()
更有意义,不涉及任何任务,让另一个线程拥有其他排队的任务。
Does ConfigureAwait(false) really avoid the creation of an extra thread?
完全没有。它只是指出不应在调用者的上下文中执行延续。
Is there any performance benefit in using Task.Run in an async Web API Controller for cpu-bound operations?
零。 None。事实上,您正在通过生成新线程来阻碍性能。在 Web 应用程序的上下文中,生成线程与 "background" 中的 运行 不同。这是由于网络请求的性质。当有传入请求时,会从池中取出一个线程来为请求提供服务。使用异步允许线程在请求结束前 returned,if 并且仅当线程处于等待状态时,即空闲。产生一个线程来处理,有效地空闲主线程,允许它 returned 到池中,但你仍然有一个活动线程。将原始线程返回到池中此时没有任何作用。然后,当新线程完成其工作时,您必须请求从池中返回一个主线程,最后 return 响应。在 所有 工作完成之前,无法 returned 响应,因此无论您使用 1 个线程还是一百个线程,异步或同步,都无法 returned 响应直到一切结束。因此,使用额外的线程只会增加开销。
Does ConfigureAwait(false) really avoid the creation of an extra thread?
不,或者更恰当地说,不是那个。 ConfigureAwait
只是一个优化提示,只判断线程跳转之间是否保持原来的上下文。总之,它与线程的创建无关,至少在 ASP.NET 应用程序的上下文中,无论哪种方式对性能的影响都可以忽略不计。
Is there any performance benefit in using
Task.Run
in an async Web API Controller for cpu-bound operations?
没有。 CPU绑定与否并不重要。
Task.Run
将工作卸载到 ThreadPool
线程。 Web api 请求已经使用了一个 ThreadPool
线程,因此您只是通过无缘无故地卸载到另一个线程来限制可伸缩性。
这在 UI 应用程序中很有用,其中 UI 线程是一个特殊的单线程。
Does ConfigureAwait(false) really avoid the creation of an extra thread?
它不会以某种方式影响线程创建。它所做的只是配置是否在捕获的 SynchronizationContext
上恢复。
您还需要考虑一件事。正如您所说,您的 api 正在执行 CPU 密集型任务,然后 async/await 帮助 运行 另一个线程中的进程并释放您的应用程序池以用于另一个请求。意味着如果您正确使用 async/await,您的网站 api 每秒可以处理更多的请求。
在你的例子中 this.CalculateSync(param1, param2)
是非异步方法所以要异步调用它你必须使用 Task.Run.
我建议删除 .ConfigureAwait(false)
,因为这实际上会降低您的表现。