async被称赞为ASP.NET的原因之一是遵循Nodejs异步平台,这导致更多的可伸缩性,释放线程来处理后续请求等。
但是,我已经读过在Task.Run中包装CPU绑定代码会产生相反的效果,即为服务器增加更多开销并使用更多线程。
显然,只有真正的异步操作才会受益,例如发出网络请求或调用数据库。
所以,我的问题如下。关于行动方法何时应该是异步的,是否有任何明确的指导?
答案 0 :(得分:4)
ASP.NET请求在线程池线程中处理。 CPU绑定的异步操作也是如此(Task.Run
)。
在ASP.NET中调度对线程池线程的异步调用将导致将线程池线程返回到线程池并获取另一个线程池线程。一个运行代码,最后,将该线程返回到线程池并获取另一个线程以返回到ASP.NET上下文。将会有很多线程切换,线程池管理和ASP.NET上下文管理,这将使该请求(以及整个应用程序)变慢。
大多数时候,人们想出在Web应用程序上执行此操作的原因最终是因为Web应用程序正在做一些它不应该做的事情。
答案 1 :(得分:4)
Cleary先生认为在异步代码中包装CPU绑定操作毫无结果。
不完全是,在ASP.NET应用程序中包装CPU绑定异步代码与在例如WPF桌面应用程序中执行此操作之间存在差异。让我用你的这个陈述来建立我的答案。
您应该在脑海中(最简单的形式)对异步操作进行分类:
我认为通过阅读Stephen Cleary的帖子,您已经了解异步操作的工作方式是当您进行CPU绑定操作时,该操作会传递给线程池线程,完成后,程序控制返回到它启动的线程(除非你进行.ConfigureAwait(false)
调用)。当然,只有在实际存在异步操作时才会发生这种情况,因为我在this question中也想知道自己。
另一方面,当谈到阻止操作时,事情会有所不同。在这种情况下,当异步执行代码的线程被阻塞时,运行时会通知它,并且"暂停"该线程正在完成的工作:保存所有状态,以便以后可以继续,然后该线程用于执行其他操作。当阻塞操作准备就绪时 - 例如,网络呼叫的答案已经到达 - 然后(我不知道运行时如何处理或注意它,但我试图为您提供一个高 - 级别说明,因此并非绝对必要)运行时知道您启动的操作已准备好继续,状态已恢复,您的代码可以继续运行。
有了这些说法,两者之间有一个重要的区别:
对于直接面向用户的应用程序,例如,WPF应用程序,如果您在主线程(GUI线程)上执行长时间运行的CPU操作,那么GUI线程显然很忙因此对用户似乎没有反应,因为 通常由GUI线程 处理的任何交互只是在消息队列中排队,并且在CPU之前不会被处理 - 绑定操作完成。
但是,对于ASP.NET应用程序, 这不是问题 ,因为应用程序不直接面向用户,所以他看不到它没有反应。为什么你通过将工作委托给另一个线程来获得任何东西是因为那仍然会占用一个线程,否则可以做其他工作,因为,无论需要做什么都必须完成,它不能神奇地成为为你完成。
按照以下方式考虑:你和朋友在一个厨房里(你和你的朋友是一对一的线程)。你们俩正准备订购食物。你可以告诉你的朋友把洋葱切成小块,即使你把洋葱切成洋葱,也可以处理肉的调味,你的朋友会把洋葱切成小块,因此他不能同时调味肉。如果你没有把切洋葱的工作委托给他(你已经开始),但让他做调味,那么同样的工作也会做,除了你会节省一点时间,因为你不会# 39; t需要交换工作环境(本例中的砧板和刀具)。所以简单地说,你只是通过交换上下文而造成一些开销,而无响应的问题对用户来说是不可见的。 (只要他收到结果,客户既不会看到也不会关心你们哪一个工作。)
话虽如此,我在顶部概述的分类可以通过用#34替换ASP.NET应用程序来改进;无论应用程序没有直接可见的用户界面,因此无法对它们显示无响应&# 34。