1. 程式人生 > >[dotNET]“ThreadPool 物件中沒有足夠的自由執行緒來完成操作”的現象和解決辦法

[dotNET]“ThreadPool 物件中沒有足夠的自由執行緒來完成操作”的現象和解決辦法

ThreadPool 物件中沒有足夠的自由執行緒來完成操作”的現象和解決辦法

編寫者:鄭昀@Ultrapower 20050406

其實微軟有一篇《非同步 HttpWebRequest、介面實現及其他》對此種現象解釋得非常清楚,我這邊只是做一個筆記。

最常見的就是使用HttpWebRequest的時候,呼叫Send方法出現這種錯誤,這是因為:

因為dotNETThreadPool中提供了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);

}

您可以看出,

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問題之外,別的地方也會發生此問題。

微軟演示了以下的程式:

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

{

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