[dotNET]“ThreadPool 物件中沒有足夠的自由執行緒來完成操作”的現象和解決辦法
“ThreadPool 物件中沒有足夠的自由執行緒來完成操作”的現象和解決辦法
編寫者:鄭昀@Ultrapower 20050406
其實微軟有一篇《非同步 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 |
需要知道的第一件事情是,在 Microsoft.NET Framework 1.x 版中,HttpWebRequest 從來不會發出同步請求。我這樣說是什麼意思呢?讓我們來看一看中為 HttpWebRequest.GetResponse 編寫的程式碼,此處顯示的程式碼省略了檢視以前是否檢索了該響應的程式碼和計算超時的程式碼:
public override WebResponse GetResponse() {
IAsyncResult asyncResult = BeginGetResponse(null, null);
return EndGetResponse(asyncResult);
}
您可以看出,
作為該問題的替代解決方案,Framework 小組實現了您正在研究的異常。在 BeginGetResponse
好訊息是,在 .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問題之外,別的地方也會發生此問題。
微軟演示了以下的程式:
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 { // Set number of threads to be created for testing. int testThreads = 55; for(int i=0;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 |
編寫者:鄭昀@Ultrapower