其实微软有一篇《异步
HttpWebRequest
、接口实现及其他》
对此种现象解释得非常清楚,我这边只是做一个笔记。
最常见的就是使用
HttpWebRequest
的时候,调用
Send
方法出现这种错误,这是因为:
因为
dotNET
的
ThreadPool
中提供了
25
个自由线程
/CPU
(可以在
machine.config
中修改此数字限制),所以一旦都被占用了,就会报告
InvalidOperationException
异常,异常提示为:
System.InvalidOperationException: There were not enough free threads in the ThreadPool object to complete the operation
|
微软的
Stephen Toub
是这么说的:
需要知道的第一件事情是,在
Microsoft.NET Framework 1.x
版中,
HttpWebRequest
从来不会发出同步请求。我这样说是什么意思呢?让我们来看一看
Shared Source CLI (SSCLI)
中为
HttpWebRequest.GetResponse
编写的代码,此处显示的代码省略了查看以前是否检索了该响应的代码和计算超时的代码:
public override WebResponse GetResponse() {
IAsyncResult asyncResult = BeginGetResponse(null, null);
return EndGetResponse(asyncResult);
}
您可以看出,
HttpWebRequest.GetResponse
只是
BeginGetResponse
和
EndGetResponse
对周围的包装。它们是异步运行的,这意味着,
BeginGetResponse
从中发出实际
HTTP
请求的线程不同于调用它的线程,而且在该请求完成之前,
EndGetResponse
会阻塞。这样做的实际结果是,
HttpWebRequest
将每个出站请求的
ThreadPool
排入工作项队列中。因此,我们知道
HttpWebRequest
使用来自
ThreadPool
的线程。
作为该问题的替代解决方案,
Framework
小组实现了您正在研究的异常。在
BeginGetResponse
的最后(就在工作项被排入队列之前),使用
System.Net.Connection.IsThreadPoolLow
来查询池中有多少个可用工作线程。如果数量过少(少于
2
个),将会引发
InvalidOperationException
。
好消息是,在
.NET Framework 2.0
中,用
HttpWebRequest.GetResponse
发出的同步请求是真正同步的,这样该问题就不复存在了,从而使得您可以将调用
GetResponse
的方法列入您的核心内容。不太好的消息是,您仍然会受到
1.x
版本中这一问题的困扰。一种解决方案是(正如您所指出的),显式限制已经排入队列的工作项的数量或在任何时间内在
ThreadPool
中执行的工作项的数量。要实现这一方法,需要有一种方法来跟踪当前有多少个未完成的工作项,并在达到预定界限之前,阻塞新的请求。
我们可以这么判断当前的自由线程数目:
int
wt;
int
ct;
int
Count=0;
while
(true
)
{
if
(Count++>20)
break
;
ThreadPool.GetAvailableThreads(out
wt,out
ct);
if
(wt<5)
{
Thread.Sleep(1000);
continue
;
}
else
break
;
}
|
除了这个
HttpWebRequest
问题之外,别的地方也会发生此问题。
在
http://support.microsoft.com/default.aspx?scid=kb;en-us;815637
微软演示了以下的程序:
using
System;
using
System.IO;
using
System.Net;
using
System.Text;
using
System.Threading;
using
System.Net.Sockets;
namespace
ThreadPoolException
{
class
Class1
{
public
static
void
Main()
{
// Set number of threads to be created for testing.
int
testThreads = 55;
for
(int
i=0;i<testThreads;i++)
{
ThreadPool.QueueUserWorkItem(new
WaitCallback(PoolFunc));
}
Console.ReadLine();
}
static
void
PoolFunc(object
state)
{
int
workerThreads,completionPortThreads;
ThreadPool.GetAvailableThreads(out
workerThreads,
out
completionPortThreads);
Console.WriteLine("WorkerThreads: {0}, CompletionPortThreads: {1}",
workerThreads, completionPortThreads);
Thread.Sleep(10000);
string
url ="http://www.msn.com";
HttpWebRequest myHttpWebRequest ;
HttpWebResponse myHttpWebResponse=null
;
// Creates an HttpWebRequest for the specified URL.
myHttpWebRequest = (HttpWebRequest)WebRequest.Create(url);
// Sends the HttpWebRequest, and waits for a response.
myHttpWebResponse = (HttpWebResponse)myHttpWebRequest.GetResponse();
myHttpWebResponse.Close();
}
}
}
|
程序输出如下:
WorkerThreads: 9, CompletionPortThreads: 1000
WorkerThreads: 8, CompletionPortThreads: 1000
WorkerThreads: 7, CompletionPortThreads: 1000
WorkerThreads: 6, CompletionPortThreads: 1000
WorkerThreads: 5, CompletionPortThreads: 1000
WorkerThreads: 4, CompletionPortThreads: 1000
WorkerThreads: 3, CompletionPortThreads: 1000
WorkerThreads: 2, CompletionPortThreads: 1000
WorkerThreads: 1, CompletionPortThreads: 1000
未处理的异常:
System.InvalidOperationException: ThreadPool
对象中没有足够的自由
线程来完成操作。
at System.Net.HttpWebRequest.BeginGetResponse(AsyncCallback callback, Object
state)
at System.Net.HttpWebRequest.GetResponse()
at ThreadPoolException.Class1.PoolFunc(Object state) in c:\threadpoolexception\threa
dpoolexception\class1.cs:line 41
Feedback
2005-12-05 21:12 by
bnflower[未注册用户]
为什么要关闭myHttpWebResponse.Close()?
我试了这段代码,连接到内网的服务器。发现如果不myHttpWebResponse.Close();只能连接上2个线程,其他的都阻塞知道超时,这是为什么呢?
我不Close是因为随后我需要这个连接来下载服务器端的文件。
2005-12-06 10:19 by
让变化成为计划的一部分
建议好好阅读http://msdn.microsoft.com/library/default.asp?url=/library/en-us/cpref/html/frlrfsystemnethttpwebresponseclassclosetopic.asp
,看看close方法到底是为什么要调用的。
另外,建议你深入了解windows客户端web request长久以来的一个潜规则,不管是dotnet的,还是以前的msxml.XMLHttpRequest,都遵循这样的一个规则,客户端发起http请求最多并发2个/4个,其他的将阻塞。
你可以看看我的这篇文档:
http://blog.csdn.net/zhengyun_ustc/archive/2002/05/20/12652.aspx
上面明确列出:
Because XMLHTTP is designed for single-user client applications and
is based on WinInet, it enforces strict limits on the number of
simultaneous connections to a given server.
The limit is either 2 or 4 (depending on HTTP version).
之后的其他MS客户端WebRequest应用也都遵循这个设计规则。
2005-12-06 10:26 by
让变化成为计划的一部分
更进一步的解释是:
通常,人们忘记了request创建的underlying connection并没有被释放,直到你调用response的close()方法。
这样,你将hit the connection limit,而且不再有web request能够进行。
让我们看下面的例子:
for(int i=0; i < 3; i++) {
HttpWebRequest r = WebRequest.Create(“http://www.microsoft.com
“) as HttpWebRequest;
HttpWebResponse w = r.GetResponse() as HttpWebResponse;
}
上面的代码是有问题的,因为第三个request将一直挂起在GetResponse()语句上。
如果你调用Response.Close() 关闭了response,underlying connection才会被释放,第三个request才会成功。
记住,这是一个潜规则,而且这是MS专门设计成这样的。
2005-12-06 10:38 by
让变化成为计划的一部分
Feroze Daud said :
A tale of threads
Today's lesson is about thread interaction between Asp.Net and the HttpWebRequest object of the System.Net namespace.
The CLR provides a threadpool. This facility provides threads for
normal work ( worker threads ), I/O work ( I/O threads) and Timer
Threads. Normally, the limit of threads for managed processes is set as
follows:
WorkerThreads: 25 per CPU
I/O Threads: 1000
Asp.Net uses worker threads to dispatch incoming requests to page
handlers (ASPX/ASMX etc). If you write an ASPX page, your request will
run on a worker thread.
It turns out that System.Net also uses the threadpool. Worker
Threads are used to kick of connect's to servers, and I/O threads are
used to service I/O completions for Sockets.
Normally, people create middle tier applications by creating a
WebRequest inside of their Page_Load(), and then doing a GetResponse().
Normally, a synchronous function (like GetResponse()) would complete on
the same thread on which it was invoked. However, GetResponse() is
actually implemented internally as follows:
void GetResponse() {
IAsyncResult ar = BeginGetResponse(null,null);
ar.WaitOne();
return EndGetResponse(ar);
}
It so happens that while you are waiting for the operation to
complete, another thread is being spawned iternally to service the
request, by kicking off the connect to the server, parsing the response
etc.
If you have a page which is under high load, then you can run into
potential deadlock issues. The user's code is running on a worker
thread. When the user does a GetResponse(), another threadpool worker
thread is used internally to kick off the operation. Imagine that there
are 25 threadpool worker threads which are executing asp.net code (eg:
page handlers etc). One of these threads is executing a page which is
trying to do the webrequest.GetResponse().
System.Net will do a ThreadPool.QueueUserWorkItem() to complete the
GetResponse(). However, since the CLR threadpool is exhausted (
uniprocessor limit is 25), CLR will not create a new thread to service
the request. If no thread becomes available, then the GetResponse() will
never complete. At the same time, the worker thread that asp.net is
running the user code on, will not complete either. Now multiply this
situation across all threads, and you have a classic deadlock.
This problem is mitigated somewhat in the V1 & V1.1 versions of
the framework. There, System.Net will query the threadpool for available
threads. If it determines that there are no free threads available to
service the request, it will throw an exception, so that the users code
can handle it.
Note that this problem can also happen in standalone applications.
So, the next time you get an exception from GetResponse() or
GetRequestStream(), saying that “There are no available free threads to
service this operation“ it is by design :-)
posted on Sunday, February 01, 2004 7:20 PM by Feroze Daud
2005-12-11 12:18 by
bnflower[未注册用户]
在MSND上我查到了HttpWebRequest有一个属性ServicePoint。
ServicePoint类有一个成员ConnectionLimit ,通过设置这个值可以改变客户端向所连接的Web服务请求的并发连接数。
request =(HttpWebRequest)WebRequest.Create(strUrl);
request.ServicePoint.ConnectionLimit = 10; //并发连接数为10个
这样
for(int i=0; i < 3; i++) {
HttpWebRequest r = WebRequest.Create(“http://www.microsoft.co
“)as HttpWebRequest;
HttpWebResponse w = r.GetResponse() as HttpWebResponse;
}
就不会在第三个r.GetResponse() as HttpWeResponse堵塞了。
看来似乎连接数的发起者是由客户端来决定的(在服务器所允许的最大连接数大于客户端请求连接数的情况下),通过这个可以创建多条与Web服务器的连接数了。
我没有使用过msxml.XMLHttpRequest,不知道是否在msxml.XMLHttpRequest也存在同样的情况。
2005-12-11 22:47 by
让变化成为计划的一部分
你说得对。
在http://blog.joycode.com/musicland/archive/2005/04/16/48690.aspx
,有人回复也提到类似的解决方法:“
re: Concurrent connection limit 2005-4-17 0:59 Wang Ting
方法三:
ServicePointManager.DefaultConnectionLimit = 1000;
方法四:
protected override WebRequest GetWebRequest(Uri uri) {
HttpWebRequest req = (HttpWebRequest)base.GetWebRequest(uri);
ServicePoint currentServicePoint = req.ServicePoint;
currentServicePoint .ConnectionLimit = 1000;
return req;
}
”。
2005-12-11 22:49 by
让变化成为计划的一部分
上面提到的方法可以用这样的代码测试:
ServicePointManager.DefaultConnectionLimit = 4;
string url = "http://www.microsoft.com/";
Uri uri = new Uri(url);
ServicePoint spSite = ServicePointManager.FindServicePoint(uri);
spSite.ConnectionLimit = 4;
HttpWebRequest request;
Stream[] ns = new Stream[4];
for(int i=0;i<4;i++)
{
request = (HttpWebRequest)HttpWebRequest.Create(url);
ns[i] = request.GetResponse().GetResponseStream();
}
2005-12-11 22:56 by
让变化成为计划的一部分
你可能要看
http://blogs.msdn.com/feroze_daud/archive/2004/11/22/268277.aspx
,
“To mitigate this, you want to use a property on the webrequest
called UnsafeAuthenticatedConnectionSharing . Setting this property will
cause HttpWebRequest to reuse authenticated connections (making sure
that it honors ServicePoint.ConnectionLimit).
Security Note: You dont want to use this property lightly. It has security consequences.
”。
另外,
“对 Internet 资源打开的连接数可能对网络性能和吞吐量有显著的影响。默认情况下,System.Net
对每个主机的每个应用程序使用两个连接。设置应用程序的 ServicePoint 中的 ConnectionLimit
属性可为特定主机增加此数目。设置 ServicePointManager.DefaultPersistentConnectionLimit
属性可为所有主机增加此默认值。”
分享到:
相关推荐
在`CheckThreadPool`方法中,我们通过`ThreadPool.GetAvailableThreads`获取当前可用的工作者线程(workerThreads)和完成端口线程(completionPortThreads)数量。`ThreadPool.GetMaxThreads`则用于获取线程池的...
从提供的文件名"ThreadMutex.cpp"和"ThreadMutex.e"来看,这些文件可能包含了易语言中关于线程互斥的实现示例。在实际项目中,你可以参考这些代码了解如何在易语言中创建、使用和管理Mutex,以及如何在多线程环境中...
在C#编程中,线程(Thread)、线程池(ThreadPool)和任务(Task)是并行处理和异步操作的重要组成部分。理解它们的工作原理和使用方法对于优化应用程序的性能至关重要。下面将详细阐述这三个概念及其相关知识点。 ...
Thread线程和ThreadPool线程池 Thread:我们可以开启一个线程。但是请大家记住:线程开启会在空间和时间上有不小的开销。所以,不能随便开。 ThreadPool:会根据你的CPU的核心数开启一个最合适的线程数量。如果你...
在C#编程中,线程(Thread)和线程池(ThreadPool)是处理并发操作的重要概念,尤其是在开发Windows Forms(WinForm)应用程序时。本文将详细介绍C#中Thread和ThreadPool的基本用法及其应用场景。 ## 1. 线程...
在Linux操作系统中,多线程是一种并发编程技术,它允许程序同时执行多个任务。线程池是这种技术的一种优化方式,它可以有效地管理线程资源,提高系统的效率和响应性。`Linux_thread_pool.zip`中的`linux_threadpool....
其中,`MyMethod`是需要在线程中执行的方法。 **线程同步**是防止多个线程同时访问同一资源造成数据不一致的重要手段。C#提供了多种同步机制,如`Mutex`、`Semaphore`、`Monitor`(锁)以及`lock`关键字。例如,...
在.NET程序设计中,线程是使用Thread类(或Timer类(线程计数器)、ThreadPool类(线程池))来处理的,这些类在System.Threading命名空间中: using System.Threading; Thread类:(实现线程的主要方法)一个...
在C++11中,我们可以使用`std::thread`类来创建和管理线程。线程池管理器还需要一个任务队列来存储待执行的任务。 2. **任务队列**:线程池中的任务不是直接提交给某一线程执行的,而是先放入一个任务队列。当线程...
本实例将详细讲解如何利用Thread类来创建和管理线程。 首先,理解线程的基本概念至关重要。线程是程序执行的最小单元,每个线程都拥有自己的内存空间,可以独立执行代码。在VB中,多线程通常用于执行耗时操作,如...
在C#编程中,多线程技术是一种关键的性能优化手段,尤其对于处理耗时操作时,能够确保用户界面(UI)的响应性,避免出现界面卡死的现象。本解决方案将深入探讨如何利用C#的多线程特性来解决这个问题。 一、线程基础...
在C#中,可以使用`System.Threading.Thread`类来创建和管理线程。通过创建新的线程实例并调用其`Start()`方法,我们可以在后台执行耗时任务,避免阻塞主线程(UI线程),从而保持用户界面的流畅性。例如: ```...
System.Threading命名空间中的Thread类是实现多线程的基础,提供了创建、控制和管理线程的方法。本文将深入探讨C#多线程编程,包括Thread类的使用以及线程同步的相关知识。 ### 1. Thread类介绍 Thread类是C#中处理...
`Thread`类是.NET框架中的核心组件,用于创建和管理线程。本篇文章将详细探讨如何通过`Thread`类在C#中建立线程,并深入理解线程的工作原理及相关知识点。 首先,创建一个新的线程通常涉及到以下几个步骤: 1. **...
标题中的"ThreadPool"指的是线程池,这是一个编程概念,特别是在多线程编程中非常关键。线程池是一种线程使用模式,它维护着一个工作线程的集合,用于执行一系列的任务。通过线程池,可以有效地管理和控制并发执行的...
- ThreadPool:表示整个线程池对象,它负责管理所有线程和任务队列。 - TaskQueue:任务队列类,负责存放待执行的任务。 - WorkerThread:工作线程类,表示线程池中的单个线程,它负责从任务队列中取出任务并执行。 ...
例如,在一个图形用户界面程序中,我们可以使用Thread类来创建一个独立的线程,该线程可以在后台执行一些耗时的操作,而不影响主线程的执行。 三、线程管理 线程管理是多线程编程中一个非常重要的概念,合理地管理...
创建一个线程通常通过`std::thread`类来完成。例如: ```cpp #include #include <thread> void workerFunction() { // 线程执行的代码 std::cout << "Worker thread executing" ; } int main() { std::thread...
在PyQt5中,通常使用`QMutex`或`QSemaphore`等同步对象来解决这些问题。 总的来说,PyQt5的多线程编程结合进度条的使用,能够帮助开发者构建高效、用户友好的应用程序。理解并熟练掌握这些技术,对于编写高质量的...
在VB(Visual Basic)编程环境中,多线程操作是一个关键的概念,它允许程序同时执行多个独立的任务,从而提高应用程序的效率和响应性。在标题"VB多线程操作"中,我们聚焦的是如何在VB中实现和管理多线程。 多线程在...