【发布时间】:2014-08-09 08:53:51
【问题描述】:
我有一个 aync 的 ASP MVC 调用,因为它需要进行异步调用。我注意到有几个慢速同步调用(例如数据库访问)。该方法需要所有返回的数据都可用才能继续。
我想用 Task.Run 包装同步调用并等待所有这些调用。
包装慢速同步调用有意义吗?如果只有同步调用怎么办?
【问题讨论】:
标签: c# asp.net-mvc async-await
我有一个 aync 的 ASP MVC 调用,因为它需要进行异步调用。我注意到有几个慢速同步调用(例如数据库访问)。该方法需要所有返回的数据都可用才能继续。
我想用 Task.Run 包装同步调用并等待所有这些调用。
包装慢速同步调用有意义吗?如果只有同步调用怎么办?
【问题讨论】:
标签: c# asp.net-mvc async-await
不要使用Task.Run 来并行化服务器端代码中的工作,除非您希望同时服务的客户端请求数量非常少。否则,您可能会加快单个请求的处理速度,但是当有很多用户时,您会损害 Web 应用程序的可伸缩性。
【讨论】:
Task.Run,则不会释放线程。至于包装单个同步调用,它根本没有意义——你不妨在当前线程上做。至于并行包装多个调用 - 你会损害可伸缩性。
Task.Run)和阻塞池线程可能会对可伸缩性产生非常严重的影响。还有臭名昭著的ThreadPool 口吃行为要记住,这里有一个related post 有一些研究。毕竟,为什么要加快单个客户端的请求处理速度(通过使用Task.Run 分叉阻塞调用),却因为没有空闲池线程而让其他客户端等待?
为了尝试扩展@Noseratio,旋转线程以“加速”同步工作的规模非常糟糕。
要记住的重要一点是,在 ASP.NET 中使用 Task.Run 是非常危险的,因为运行时不知道您排队了需要完成的工作,并且 IIS 可能会不时尝试回收您的应用程序,这将无意中导致工作突然中断。
如果您使用的是 .NET Framework 4.5.2,则可以通过 HostingEnvironment.QueueBackgroundWorkItem 找到解决方案。您可以在Fire and Forget on ASP.NET 中阅读有关它的更多信息。如果没有,请阅读 Returning Early from ASP.NET Requests 以获取自定义实现。这两篇优秀的文章都出自@StephanCleary
【讨论】:
awaiting,但 IIS 没有理由不尝试回收 eveb。我不是在谈论 async void 或 fire and forget,我在谈论在 ASP.NET 中一般使用Task.Run
async Web API 方法中的第一个await 时,将释放主要操作线程(HTTP 请求已登陆以进行处理的初始线程)。但是,此时请求仍处于挂起状态(因为await),因此不会回收 IIS 进程/应用程序域。在内部,跟踪is done via AsyncManager。然而,像await Task.Run(DoWork) 这样的东西在 ASP.NET 中没有意义:它只是释放一个线程并获取另一个线程。
如果任务是独立的(不依赖于其他任务的数据),并且可以独立执行,那么可以,一定要异步执行它们。如果它们是实体框架,并且是第 6 版,那么它提供了要调用的异步方法,并且您不必将它们包装在 Task.Run 中。
即使任务不是独立的,您仍然可以对它们进行排序,以提高执行时的效率。
然而,是否使用Task.Run 是一个重要的区别。 Task.Run 将使用 ThreadPool 线程,如果它正在执行同步操作,则会阻塞,因此会减少应用程序可用的 ThreadPool theads 的数量。如果您有很多用户,并且您正在执行许多任务,这可能是一个问题。
尝试查找异步 api 而不是使用 Task.Run。
【讨论】: