首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >ASP.NET框架中的异步页面-其他线程在哪里,它是如何重新连接的?

ASP.NET框架中的异步页面-其他线程在哪里,它是如何重新连接的?
EN

Stack Overflow用户
提问于 2010-04-04 18:14:02
回答 4查看 2.7K关注 0票数 10

对不起,关于异步操作的这个愚蠢的问题。我就是这么理解的。

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

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

( 1)这些其他线程在哪里?还有其他线程池吗?

2)如果ASP.NET喜欢在另一个线程池(?)中创建新线程,为什么不增加主工作池中的线程数量--它们反正都在同一台机器上运行?我不认为将该请求移动到另一个线程池有什么好处。内存/CPU应该是相同的,对吗?

3)如果主线程将请求传递给另一个线程,为什么请求不被断开?它神奇地将请求传递给其他地方的另一个辅助线程,当长时间运行的进程完成时,它从主工作池中选择一个线程并向客户端发送响应。我是amazed...but,那是怎么回事?

EN

回答 4

Stack Overflow用户

回答已采纳

发布于 2010-04-05 18:24:16

您没有说明您使用的是哪个版本的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管道编写ISAPI或本机处理程序。您可能只是在使用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上的MaxConcurrentRequestsPerCPU v4.0中的ASP.NET的新默认值是5000。

来回答你的三个问题:

( 1)第一,一个小底漆。每个CPU一次只能执行一个线程。当你有更多的时候,你付出了代价--每次CPU切换到另一个线程时都需要一个上下文切换,而这些代价是昂贵的。但是,如果一个线程在等待work...then时被阻塞,那么切换到另一个线程是有意义的,这个线程现在就可以执行。

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

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

好的,那么你的第一个问题是“这些其他线程在哪里?还有其他线程池吗?”这要看情况了。在.NET框架中运行的大多数代码都使用CLR ThreadPool,它由两种类型的线程组成,即工作线程和i/o完成线程。不使用CLR ThreadPool的代码呢?嗯,它可以创建自己的线程,使用自己的线程池,或者它想要的任何东西,因为它可以访问操作系统提供的Win32 API。基于我们刚才讨论的内容,线程从何而来并不重要,就操作系统和硬件而言,线程就是线程。

2)在你的第二个问题中,你说:“我看不出把请求转移到另一个线程池有什么好处。”您正确地认为,除非您要弥补您为切换而执行的昂贵的上下文切换,否则切换是没有好处的。这就是为什么我给远程服务器提供了一个缓慢的HTTP或SOAP请求的例子,作为切换的好理由。顺便说一句,ASP.NET不创建任何线程。它使用CLR ThreadPool,该池中的线程完全由CLR管理。它们在确定何时需要更多线程方面做得很好。例如,这就是为什么ASP.NET可以轻松地从并发执行1个请求扩展到并发执行300个请求,而无需执行任何操作。传入的请求通过对ThreadPool的调用提交给QueueUserWorkItem,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我们完成了任务,IIS将向客户端发送最后的字节,如果不使用“保持活动”,则关闭连接。

你好,托马斯

票数 11
EN

Stack Overflow用户

发布于 2010-04-05 02:04:04

ASP.NET中的异步页面使用异步回调,异步回调使用线程池,--用于服务ASP.NET请求的线程池。

然而,这并不是很简单。.NET ThreadPool有两种类型的线程--工作线程和I/O线程。I/O线程使用所谓的I/O完成港,这是一种无线程或与线程无关的方法,用于等待文件句柄上的读/写操作完成,随后运行回调方法。

(请注意,文件句柄不一定指磁盘上的文件;就Windows而言,它也可以是套接字、管道等)。

一个典型的.NET web开发人员并不需要真正了解这一切。当然,如果您正在编写一个实际的web服务器或任何类型的网络服务器,那么您肯定需要了解这些信息,因为它们是处理数百个传入连接的唯一方法,而不会产生数百个线程来服务它们。如果您感兴趣,有一个托管I/O完成端口教程(CodeProject)。

无论如何,回到主题;当您与线程池进行高级别的交互时,即通过编写:

代码语言:javascript
复制
ThreadPool.QueueUserWorkItem(s => DoSomeWork(s));

这会使而不是使用I/O完成端口。永远不会。它将工作发布到由线程池管理的普通工作线程之一。如果使用异步回调,则相同:

代码语言:javascript
复制
Func<int> asyncFunc;

IAsyncResult BeginOperation(object sender, EventArgs e, AsyncCallback cb,
    object state)
{
    asyncFunc = () => { Thread.Sleep(500); return 42; };
    return asyncFunc.BeginInvoke(cb, state);
}

void EndOperation(IAsyncResult ar)
{
    int result = asyncFunc.EndInvoke(ar);
    Console.WriteLine(result);
}

再说一次-同样的交易。在EndOperation中,您正在ThreadPool工作线程上运行。您可以通过插入以下调试代码来验证这一点:

代码语言:javascript
复制
void EndSimpleWait(IAsyncResult ar)
{
    int maxWorkers, maxIO, availableWorkers, availableIO;
    ThreadPool.GetMaxThreads(out maxWorkers, out maxIO);
    ThreadPool.GetAvailableThreads(out availableWorkers, out availableIO);
    int result = asyncFunc.EndInvoke(ar);
}

在其中插入一个断点,您将看到availableWorkersmaxWorkers少一个,而maxIOavailableIO是相同的。

但是一些异步操作在.NET中是“特殊的”,这实际上与ASP.NET无关--它们也将在Winforms或WPF应用程序中使用I/O完成端口。例子如下:

  • System.Net.Sockets.Socket (BeginReceive和大量其他BeginXYZ方法)
  • System.IO.FileStream (BeginReadBeginWrite )
  • System.ServiceModel.ClientBase<T> (BeginInvoke )
  • System.Net.WebRequest (BeginGetResponse)

以此类推,这远不能算是一个完整的清单。基本上,.NET框架中几乎每个公开自己的BeginXYZEndXYZ方法并可以执行任何I/O的类都可能使用I/O完成端口。这是为了让应用程序开发人员更加容易,因为I/O线程在.NET中很难实现自己。

我的猜测是,.NET框架设计人员故意选择发布I/O操作(与只编写ThreadPool.QueueUserWorkItem的工作线程相比),因为如果您不知道如何正确使用它们,相对来说是“危险的”;相比之下,在Windows中生成这些操作非常简单。

和前面一样,您可以验证某些调试代码发生了什么:

代码语言:javascript
复制
WebRequest request;

IAsyncResult BeginDownload(object sender, EventArgs e,
    AsyncCallback cb, object state)
{
    request = WebRequest.Create("http://www.example.com");
    return request.BeginGetResponse(cb, state);
}

void EndDownload(IAsyncResult ar)
{
    int maxWorkers, maxIO, availableWorkers, availableIO;
    ThreadPool.GetMaxThreads(out maxWorkers, out maxIO);
    ThreadPool.GetAvailableThreads(out availableWorkers, out availableIO);
    string html;
    using (WebResponse response = request.EndGetResponse(ar))
    {
        using (StreamReader reader = new
            StreamReader(response.GetResponseStream()))
        {
            html = reader.ReadToEnd();
        }
    }
}

如果您通过这一步,您将看到线程状态是不同的。availableWorkers将与maxWorkers相匹配,但availableIOmaxIO少一个。这是因为您在I/O线程上运行。这也是为什么您的不应该在异步回调中执行任何昂贵的计算,--在I/O完成端口上发布CPU密集型的工作效率很低,而且很糟糕。

所有这些都解释了为什么强烈建议您在ASP.NET中执行任何I/O操作时使用异步页面。这种模式只对I/O操作有用;非I/O异步操作最终将被发布到ThreadPool中的工作线程中,您最终仍然会阻塞后续的ASP.NET请求。但是,您可以生成几乎无限数量的异步I/O操作,而无需再考虑;在I/O完成和回调准备就绪之前,这些操作根本不会使用任何线程。

因此,总结一下--只有一个ThreadPool,但是其中有不同类型的线程,如果您执行缓慢的I/O操作,那么使用I/O线程要高效得多。它与CPU或内存无关,而是与I/O和文件句柄有关。

至于第三个问题,这并不是一个“为什么请求不被断开”的问题,更像是一个“为什么会被中断”的问题。套接字不会因为当前没有线程发送或接收数据而关闭,就像如果没有人来迎接客人,您的前门就不会自动关闭一样。如果服务器没有响应它们,客户端操作可能会超时,随后可能会选择从它们的端断开连接,但这完全是另一个问题。

票数 10
EN

Stack Overflow用户

发布于 2010-04-05 00:51:45

1)线程在w3svc中,或者在特定版本的IIS中运行ASP.NET引擎的任何进程。

2)不知道你在这里是什么意思。实际上,您可以控制工作线程池中有多少线程。这篇文章很好:http://msdn.microsoft.com/en-us/library/ms998549.aspx

3)我认为你混淆了请求和联系.老实说,我不知道IIS的内部是如何工作的,但在同时处理多个请求的应用程序中,通常有一个主侦听线程,然后将实际工作交给子线程(而不做任何其他操作)。最初的请求并不是“断开”的,因为这些事情发生在网络协议栈的完全不同的级别上。Windows Server在TCP端口80上接受多个连接没有问题。想想TCP/IP是如何工作的,以及它正在发送多个离散的信息包这一事实。你在想“连接”,就像一根水管从龙头A到龙头B,但这当然不是它真正的工作方式。它更像一个桶,它只是收集任何溢出到里面的东西。

希望这能有所帮助。

票数 3
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/2575404

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档