asp.net - ASP.NET 框架中的异步页面 - 其他线程在哪里以及如何重新附加?

标签 asp.net asp.net-mvc asp.net-mvc-2 asp.net-2.0

很抱歉这个关于异步操作的愚蠢问题。我是这样理解的。

IIS 具有一组有限的等待请求的工作线程。如果一个请求是长时间运行的操作,它将阻塞该线程。这导致服务请求的线程更少。

解决这个问题的方法 - 使用异步页面。当一个请求进来时,主工作线程被释放,另一个线程在其他地方创建。因此,主线程能够为其他请求提供服务。当请求在另一个线程上完成时,从主线程池中选择另一个线程并将响应发送回客户端。

1) 这些其他线程在哪里?是否有另一个线程池?

2)如果 ASP.NET 喜欢在这个其他线程池中创建新线程(?),为什么不增加主工作池中的线程数 - 它们都在同一台机器上运行?我没有看到将该请求移动到另一个线程池的优势。内存/CPU应该是一样的吧?

3) 如果主线程将请求移交给另一个线程,为什么该请求不会断开连接?它神奇地将请求转交给其他地方的另一个工作线程,当长时间运行的进程完成时,它从主工作池中选择一个线程并向客户端发送响应。我很惊讶……但这如何运作?

最佳答案

您没有说明您使用的是哪个版本的 IIS 或 ASP.NET。很多人谈论 IIS 和 ASP.NET 就好像它们是一体的一样,但它们实际上是协同工作的两个组件。请注意,IIS 6 和 7 监听 I/O 完成端口,它们从 HTTP.sys 获取完成。为此使用了 IIS 线程池,它的最大线程数为 256。此线程池的设计方式使其不能很好地处理长时间运行的任务。 IIS 团队的建议是,如果您要进行大量工作,例如由 IIS 7 上的 ASP.NET ISAPI 和/或 ASP.NET“集成模式”处理程序完成,请切换到另一个线程。否则您将陷入困境启动 IIS 线程并防止 IIS 从 HTTP.sys 获取完成IIS 7 管道。您可能只是在使用 ASP.NET,在这种情况下,您对它的线程池及其工作方式更感兴趣。

有一篇博文位于 http://blogs.msdn.com/tmarq/archive/2007/07/21/asp-net-thread-usage-on-iis-7-0-and-6-0.aspx这解释了 ASP.NET 如何使用线程。请注意,对于 IIS 7 上的 ASP.NET v2.0 和 v3.5,您应该将 MaxConcurrentRequestsPerCPU 增加到 5000——这是一个错误,在这些平台上默认设置为 12。 IIS 7 上的 ASP.NET v4.0 中 MaxConcurrentRequestsPerCPU 的新默认值为 5000。

回答你的三个问题:

1)首先,一点底漆。每个 CPU 一次只能执行 1 个线程。当你有更多的时候,你就要付出代价——每次 CPU 切换到另一个线程时都需要进行上下文切换,而这些代价是昂贵的。然而,如果一个线程在等待工作时被阻塞……那么切换到另一个线程是有意义的,一个现在可以执行的线程。

因此,如果我有一个线程正在执行大量计算工作并大量使用 CPU,并且这需要很长时间,我是否应该切换到另一个线程?不!当前线程有效地使用 CPU,因此切换只会产生上下文切换的成本。

因此,如果我有一个线程向另一台服务器发出 HTTP 或 SOAP 请求并且需要很长时间,我应该切换线程吗?是的!您可以异步执行 HTTP 或 SOAP 请求,这样一旦发生“发送”,您就可以展开当前线程并且不使用任何线程,直到“接收”完成 I/O。在“发送”和“接收”之间,远程服务器很忙,因此您不需要在本地阻塞线程,而是使用 .NET Framework 中提供的异步 API,以便您可以放松和完成后通知。

好的,所以你的第一个问题是“这些其他线程在哪里?还有另一个线程池吗?”这取决于。大多数在 .NET Framework 中运行的代码使用 CLR ThreadPool,它由两种类型的线程组成,工作线程和 I/O 完成线程。不使用 CLR ThreadPool 的代码呢?好吧,它可以创建自己的线程,使用自己的线程池,或者任何它想要的东西,因为它可以访问操作系统提供的 Win32 API。根据我们之前讨论的内容,线程来自哪里并不重要,并且就操作系统和硬件而言,线程就是线程。

2) 在你的第二个问题中,你说,“我没有看到将该请求移动到另一个线程池的优势。”您认为切换没有任何优势是正确的,除非您要弥补刚刚为切换而执行的昂贵的上下文切换。这就是为什么我举了一个向远程服务器发送慢速 HTTP 或 SOAP 请求的例子,作为切换的一个很好的理由。顺便说一下,ASP.NET 不创建任何线程。它使用 CLR ThreadPool,并且该池中的线程完全由 CLR 管理。他们在确定何时需要更多线程方面做得非常好。例如,这就是为什么 ASP.NET 可以轻松地从并发执行 1 个请求扩展到并发执行 300 个请求,而无需执行任何操作。传入的请求通过调用 QueueUserWorkItem 发布到 CLR 线程池,CLR 决定何时调用 WaitCallback(请参阅 MSDN)。

3)第三个问题是,“如果主线程将请求移交给另一个线程,为什么请求没有断开连接?”好吧,当请求最初到达服务器时,IIS 从 HTTP.sys 获取 I/O 完成。然后 IIS 调用 ASP.NET 的处理程序(或 ISAPI)。 ASP.NET 立即将请求排队到 CLR 线程池,并向 IIS 返回挂起状态。这种挂起状态告诉 IIS 我们还没有完成,但一旦完成,我们就会通知您。现在 ASP.NET 管理该请求的生命周期。当 CLR ThreadPool 线程调用 ASP.NET WaitCallback(请参阅 MSDN)时,它可以在该线程上执行整个请求,这是正常情况。或者,如果请求是我们所说的异步请求,它可以切换到一个或多个其他线程——即它有一个异步模块或处理程序。无论哪种方式,请求完成都有明确定义的方式,当它最终完成时,ASP.NET 将告诉 IIS 我们已完成,如果 Keep-Alive 是,IIS 将向客户端发送最后的字节并关闭连接没有被使用。

问候,
托马斯

关于asp.net - ASP.NET 框架中的异步页面 - 其他线程在哪里以及如何重新附加?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/2575404/

相关文章:

c# - 为什么这种线程化方法不起作用?

.net - ASP.Net MVC 2 - 将所有输出合并到单个程序集中

c# - 将 Ajax 数组发送到 Controller

asp.net-mvc-2 - 获取与 MVC 项目关联的区域

c# - SQL Server 数据库不会更新来自网站的信息

c# - 找不到资源 asp.net mvc

asp.net - Moq Parent 没有默认构造函数。默认构造函数必须显式定义

asp.net-mvc - MVC3 区域中的相对内容路径

c# - 如何使用 AddHours 和 AddMinutes 在 DateTime 变量中添加时间?

c# - Asp.net MVC 休眠 : Can not modify more than one base table through a join view