【问题标题】:Async and CPU-Bound Operations with ASP.NET使用 ASP.NET 进行异步和 CPU 密集型操作
【发布时间】:2017-07-25 17:47:35
【问题描述】:

async 在 ASP.NET 中受到赞誉的原因之一是它遵循 Nodejs 异步平台,它通过释放线程来处理后续请求等带来了更大的可扩展性。

但是,我已经读过在 Task.Run 中包装 CPU 绑定代码会产生相反的效果,即给服务器增加更多开销并使用更多线程。

显然,只有真正的异步操作才会受益,例如发出网络请求或调用数据库。

所以,我的问题如下。关于何时应该异步操作方法是否有任何明确的指导?

【问题讨论】:

  • 恕我直言,async-awaitStephen Cleary 上最好的信息来源之一。 Hth
  • @EdSF 谢谢。实际上,我一直在阅读 Stephen Cleary 的博客。 Cleary 先生认为将 CPU 绑定操作封装在异步代码中是徒劳的。我将阅读您链接到的 SO 答案,看看我是否在那里学到了任何新东西。谢谢。
  • @onefoots 将链接您阅读的有关异步代码中 CPU 绑定操作徒劳无功的文章?
  • @mmcrae 这篇文章是 Stephen 讨论 Fake Async 的文章 - blog.stephencleary.com/2013/10/…Cheers

标签: asp.net-mvc async-await


【解决方案1】:

Cleary 先生认为将 CPU 绑定操作封装在异步代码中是徒劳的。

不完全是,在 ASP.NET 应用程序中包装 CPU 绑定异步代码和在 WPF 桌面应用程序中执行此操作之间存在差异。让我用你的这个陈述来建立我的答案。

您应该在脑海中将异步操作分类(以最简单的形式):

  • ASP.NET 异步方法,其中包括:
    • CPU 密集型操作,
    • 阻塞操作,例如 IO 操作
  • 直接面向用户的应用程序中的异步方法,其中包括:
    • CPU 密集型操作,
    • 和阻塞操作,例如 IO 操作。

我假设通过阅读 Stephen Cleary 的帖子,您已经了解异步操作的工作方式是,当您执行受 CPU 限制的操作时,该操作将传递到线程池线程并在完成后,程序控制返回到它开始的线程(除非您执行.ConfigureAwait(false) 调用)。当然,这只有在确实有异步操作要做时才会发生,正如我在this question 中也想知道的那样。

另一方面,当涉及到阻塞操作时,情况就有些不同了。在这种情况下,当异步执行代码的线程被阻塞时,运行时会注意到它,并“暂停”该线程正在完成的工作:保存所有状态以便稍后可以继续,然后该线程用于执行其他操作。当阻塞操作准备好时——例如,网络调用的答案已经到达——然后(我不完全知道它是如何被运行时处理或注意到的,但我试图为你提供一个高级解释,所以不是绝对必要的)运行时知道你发起的操作已经准备好继续,状态恢复,你的代码可以继续运行。

说了这么多,两者有一个重要的区别:

  • 在受 CPU 限制的情况下,即使您以异步方式启动操作,也有工作要做,您的代码无需等待任何事情。李>
  • 但是,在 IO 绑定情况或阻塞情况下,可能有一段时间您的代码只能等待,因此很有用您可以释放在此之前完成处理的线程,并在使用它的同时做其他工作(可能处理另一个请求)。

对于直接面向用户的应用程序,例如 WPF 应用程序,如果您在主线程(GUI 线程)上执行长时间运行的 CPU 操作,那么 GUI 线程显然很忙,并且因此对用户似乎没有响应,因为 通常由 GUI 线程处理 的任何交互都只会在消息队列中排队,直到 CPU 密集型才得到处理操作结束。

然而,对于 ASP.NET 应用程序,这不是问题,因为应用程序不直接面对用户,所以他看不到它没有反应。为什么通过将工作委派给另一个线程没有任何收获是因为它仍然会占用一个线程,否则它可以做其他工作,因为,好吧,无论需要做什么都必须完成,它只是不能神奇地完成你。

可以这样想:你和一个朋友在厨房里(你和你的朋友是一对一的线程)。你们两个正在准备点菜。你可以告诉你的朋友切洋葱,即使你把自己从切洋葱中解放出来,并且可以处理肉的调味,你的朋友因为切洋葱而忙碌,所以他不能同时做肉的调味。如果你没有把切洋葱的工作委托给他(你已经开始了),而是让他做调味,同样的工作也会完成,只是你会节省一点时间,因为你不会需要交换工作环境(本例中的砧板和刀具)。所以简单地说,你只是通过交换上下文造成了一些开销,而无响应的问题对用户来说是不可见的。 (只要他收到结果,客户就不会看到也不关心你们中的哪些人做哪些工作)。

话虽如此,我在顶部概述的分类可以通过将 ASP.NET 应用程序替换为“任何应用程序对用户没有直接可见的界面,因此不会显得对他们没有响应”来改进。

【讨论】:

  • 谢谢。我喜欢你的措辞“除了等待什么也做不了”。我想我想知道为什么 Nodejs 是可扩展的,每个处理请求的线程都将处理交给另一个线程。不管它是否会阻塞 IO 或 CPU 绑定。似乎服务器上的异步并不是一个很好的原因,因为我很少从我的服务器执行 WebRequest。陪审团似乎对是否使用 EF 的异步功能没有意见。我读过的坏书和好书一样多。
【解决方案2】:

ASP.NET 请求在线程池线程中处理。受 CPU 限制的异步操作也是如此 (Task.Run)。

向 ASP.NET 中的线程池线程分派异步调用将导致将线程池线程返回到线程池并获得另一个线程池线程。一个来运行代码,最后将该线程返回到线程池,并让另一个线程返回到 ASP.NET 上下文。将会有大量线程切换、线程池管理和 ASP.NET 上下文管理进行,这将使请求(以及整个应用程序)变慢。

在大多数情况下,一个在 Web 应用程序上执行此操作的原因最终是因为 Web 应用程序正在做不应该做的事情。

【讨论】:

  • 谢谢。你回答的最后一句话对我来说特别有意义。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-03-11
  • 1970-01-01
  • 1970-01-01
  • 2016-12-20
  • 2016-02-09
相关资源
最近更新 更多