其实微软有一篇《异步
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
属性可为所有主机增加此默认值。”
分享到:
相关推荐
Thread线程和ThreadPool线程池 Thread:我们可以开启一个线程。但是请大家记住:线程开启会在空间和时间上有不小的开销。所以,不能随便开。 ThreadPool:会根据你的CPU的核心数开启一个最合适的线程数量。如果你...
在.NET程序设计中,线程是使用Thread类(或Timer类(线程计数器)、ThreadPool类(线程池))来处理的,这些类在System.Threading命名空间中: using System.Threading; Thread类:(实现线程的主要方法)一个...
C# Thread、ThreadPool、Task测试,三者创建、启动的方法
C#winform程序Thread(线程)和ThreadPool(线程池)的基本用法
linux下一个多线程池的实现,可以加快线程的调度,是一个学习linux线程开发的好例子
Qt thread pool 线程池样例,信号槽,跨线程 lambda,使用样例;
线程操作的关键对象和语句 任何程序在执行时,至少有一个主线程。 Thread类有几个至关重要的方法 C#多线程学习(二) 如何操纵一个线程 Thread.ThreadState 属性 线程的优先级 C#多线程学习(三) 生产者和消费者 While...
线程应用程序的伸缩 5.1 什么是线程池管理 5.1.1 需要线程池的情况 5.1.2 线程地的概念 5.2 CLR和线程 5.2.1 CLR在线程地管理中的角色 5.2.2 线程地管理中的问题 5.2.3 线程地的大小 5.3 ThreadPool类 5.4 VB.NET中...
实现三个线程同时执行,且可以单独关闭和运行,通过进度条实现可视化演示。
本程序详细介绍了线程和线程池的用法,使用多线程进行和异步编程实现数据库操作和日志的记录
线程操作的关键对象和语句 2 任何程序在执行时,至少有一个主线程。 2 Thread类有几个至关重要的方法 3 C#多线程学习(二) 如何操纵一个线程 3 Thread.ThreadState 属性 5 线程的优先级 5 C#多线程学习(三) 生产者和...
并发执行线程thread-1和thread-2, 结果在同一时间执行了thread-1和thread-2。python实现在import _thread时,需pip install threadpool.
(原Mutex 类的程序示例有点问题, 4个子线程获取到mutex后都没有执行ReleaseMutex()方法, 导致出现:由于出现被放弃的 mutex,等待过程结束 的异常, 这个是因为需要在获取mutex的线程里面执行ReleaseMutex()方法的, ...
普通线程的创建,通过 委托ThreadStart对应的函数来执行相关操作; 通过线程池,可以直接从池中查找出空闲线程,让它执行委托WaitCallback对应函数来执行相关操作。使用时要与AutoResetEvent来并用,以在线程结束时...
Java并发编程的核心概念包括: 线程(Thread):线程是程序执行流的最小单元。...原子操作(Atomic Operations):原子操作是不可中断的操作,即在多线程环境中,这些操作要么完全执行,要么完全不执行。
我们将在这里进一步讨论一些.NET类,以及他们在多线程编程中扮演的角色和怎么编程。它们是: System.Threading.ThreadPool 类 System.Threading.Timer 类 如果线程的数目并不是很多,而且你想控制每个线程的...
耗时操作或nodejs没有提供异步模式的api(例如解密、同步的文件api)都可以在线程池中执行,业务代码只需要返回一个Promise或async函数给线程池库,至于业务逻辑做什么操作,其实都可以,比如setTimeout,异步操作,...
线程池使用std :: thread和其他C ++ 11/14标准线程实用程序的C ++线程池。 易于使用,具有一些额外功能,可以进行更多控制。auto add(Func&& func, Args&&... args) 这是将作业添加到ThreadPool 。 接受要排队的可调...
1、测试调用委托时BeginInvoke和Invoke的表现差异 2、System.Timers.Timer多线程刷新界面 3、System.Threading.ThreadPool多线程刷新界面 4、System.Threading.Thread多线程刷新界面
使用节点的新worker_thread API,池中的线程可以相互传递消息,以及对共享内存进行读写。 用法 完整的API文档可以在这里找到: : 。 如果您熟悉Java的线程池API,则应该非常熟悉: import { Executors } from ...