許多應用程序使用多個線程,但這些線程常常在休眠狀態中耗費大量的時間來等待事件發生。其餘線程可能進入休眠狀態,而且僅按期被喚醒以輪詢更改或更新狀態信息,而後再次進入休眠狀態。爲了簡化對這些線程的管理,.NET框架爲每個進程提供了一個線程池,使應用程序可以根據須要來有效地利用多個線程。一個線程監視排到線程池的若干個等待操做的狀態。當一個等待操做完成時,線程池中的一個輔助線程就會執行對應的回調函數。線程池中的線程由系統進行管理,程序員不須要費力於線程管理,能夠集中精力處理應用程序任務。程序員
線程池是一種多線程處理形式,處理過程當中將任務添加到隊列,而後在建立線程後自動啓動這些任務。線程池線程都是後臺線程。每一個線程都使用默認堆棧大小,以默認的優先級運行,並處於多線程單元中。若是某個線程在託管代碼中空閒(如正在等待某個事件),則線程池將插入另外一個輔助線程來使全部處理器保持繁忙。若是全部線程池線程都始終保持繁忙,但隊列中包含掛起的工做,則線程池將在一段時間以後建立另外一個輔助線程。但線程的數目永遠不會超過最大值。超過最大值的其餘線程能夠排隊,但它們要等到其餘線程完成後才啓動。多線程
線程池特別適合於執行一些須要多個線程的任務。使用線程池可以優化這些任務的執行過程,從而提升吞吐量,它不只可以使系統針對此進程優化該執行過程,並且還可以使系統針對計算機上的其餘進程優化該執行過程。若是須要啓動多個不一樣的任務,而不想分別設置每一個線程的屬性,則可使用線程池。app
若是應用程序須要對線程進行特定的控制,則不適合使用線程池,須要建立並管理本身的線程。框架
在如下幾種狀況下,適合於使用線程池線程:異步
(1)不須要前臺執行的線程。函數
(2)不須要在使用線程具備特定的優先級。性能
(3)線程的執行時間不易過長,不然會使線程阻塞。因爲線程池具備最大線程數限制,所以大量阻塞的線程池線程可能會阻止任務啓動。優化
(4)不須要將線程放入單線程單元。全部 ThreadPool 線程均不處於多線程單元中。spa
(5)不須要具備與線程關聯的穩定標識,或使某一線程專用於某一任務。操作系統
在多線程的程序中,常常會出現兩種狀況:一種是在應用程序中,線程把大部分的時間花費在等待狀態,等待某個事件發生,而後才能給予響應,這通常使用ThreadPool(線程池)來解決;另外一種狀況是在線程平時都處於休眠狀態,只是週期性地被喚醒,這通常使用Timer(定時器)來解決。下面對ThreadPool類進行詳細說明。
ThreadPool類提供一個線程池,該線程池可用於發送工做項、處理異步 I/O、表明其餘線程等待以及處理計時器。該類提供一個由系統維護的線程池(能夠看做一個線程的容器),該容器須要Windows 2000以上系統支持,由於其中某些方法調用了只有高版本的Windows纔有的API函數。
下面介紹一下該類所提供的方法,如表1所示。
表1 ThreadPool類的方法
方法 |
描述 |
BindHandle |
將操做系統句柄綁定到ThreadPool |
GetAvailableThreads |
檢索由GetMaxThreads方法返回的最大線程池線程數和當前活動線程數之間的差值 |
GetMaxThreads |
檢索能夠同時處於活動狀態的線程池請求的數目。全部大於此數目的請求將保持排隊狀態,直到線程池線程變爲可用 |
GetMinThreads |
檢索線程池在新請求預測中維護的空閒線程數 |
QueueUserWorkItem |
將方法排入隊列以便執行。此方法在有線程池線程變得可用時執行 |
RegisterWaitForSingleObject |
註冊正在等待WaitHandle的委託 |
SetMaxThreads |
設置能夠同時處於活動狀態的線程池的請求數目。全部大於此數目的請求將保持排隊狀態,直到線程池線程變爲可用 |
SetMinThreads |
設置線程池在新請求預測中維護的空閒線程數 |
UnsafeQueueNativeOverlapped |
將重疊的 I/O 操做排隊以便執行 |
UnsafeQueueUserWorkItem |
註冊一個等待 WaitHandle 的委託 |
UnsafeRegisterWaitForSingleObject |
將指定的委託排隊到線程池 |
經過以上方法,能夠對線程池進行設置以及相應的操做,那麼,咱們何時使用線程池呢?咱們在用Thread類調用線程時,一次只能使用一個線程來建立和刪除線程,這種方式的創建和刪除線程對CPU的使用是很頻繁的,爲了節省CPU的負荷,可使用線程池對線程進行操做,爲了使讀者更深刻的瞭解Thread類與ThreadPool類的差異。
在如下狀況下,應使用ThreadPool類:
ThreadPool類會在線程的託管池中重用已有的線程。使用完線程後,線程就會返回線程池,供之後使用。ThreadPool有25個可用的線程(每一個處理器)。
在使用線程池時,通常調用ThreadPool類的QueueUserWorkItem方法。
用戶並不須要自已創建線程,只須要把要進行的操做寫成函數,而後做爲參數傳遞給ThreadPool類的QueueUserWorkItem方法就好了,傳遞的方法是依靠WaitCallback代理對象,而線程的創建、管理、運行等工做都是由系統自動完成的,用戶無須考慮那些複雜的細節問題。ThreadPool類的用法:首先程序建立了一個ManualResetEvent對象,該對象就像一個信號燈,能夠利用它的信號來通知其它線程。
爲了讀者能更好的控制線程池,下面講解一下如何設置和讀取線程池的最大線程數和最小空閒線程數。
1.線程池的最大線程數
可排隊到線程池的操做數僅受可用內存的限制;可是,線程池限制進程中能夠同時處於活動狀態的線程數。默認狀況下,限制每一個CPU可使用25個輔助線程和1000 個I/O完成線程。
經過使用GetMaxThreads和SetMaxThreads方法能夠控制最大線程數。下面對這兩個方法的聲明進行一下說明。
(1)GetMaxThreads方法
該方法檢索能夠同時處於活動狀態的線程池請求的數目。全部大於此數目的請求將保持排隊狀態,直到線程池線程變爲可用。其語法以下:
public static void GetMaxThreads(out int workerThreads,out int completionPortThreads)
參數說明:
workerThreads:Int32,線程池中輔助線程的最大數目。
CompletionPortThreads:Int32,線程池中異步I/O線程的最大數目。
(2)SetMaxThreads方法
該方法設置能夠同時處於活動狀態的線程池的請求數目。全部大於此數目的請求將保持排隊狀態,直到線程池線程變爲可用。
[SecurityPermissionAttribute(SecurityAction.Demand, ControlThread = true)] public static bool SetMaxThreads(int workerThreads,int completionPortThreads)
參數說明:
workerThreads:Int32,線程池中輔助線程的最大數目。
CompletionPortThreads:Int32,線程池中異步 I/O 線程的最大數目。
返回值:bool型,若是更改爲功,則爲true;不然爲false。
下面經過一段代碼對GetMaxThreads和SetMaxThreads方法的應用進行一下講解,主要是顯示線程池中輔助線程的最大數和線程池中異步I/O線程的最大數,以及用戶自行設置後的線程池狀況。代碼以下:
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading; namespace ConsoleApplication1 { class Program { static void Main(string[] args) { //設置正在等待線程的事件爲終止 AutoResetEvent autoEvent = new AutoResetEvent(false); int workerThreads; int portThreads; //獲取處於活動狀態的線程池請求的數目 ThreadPool.GetMaxThreads(out workerThreads, out portThreads); //在控制檯中顯示處於活動狀態的線程池請求的數目 Console.WriteLine("設置前,線程池中輔助線程的最大數爲:" + workerThreads.ToString() + ";線程池中異步I/O線程的最大數爲:" + portThreads.ToString()); workerThreads = 10;//設置輔助線程的最大數 portThreads = 500;//設置線程池中異步I/O線程的最大數 //設置處於活動狀態的線程池請求的數目 ThreadPool.SetMaxThreads(workerThreads, portThreads); ThreadPool.QueueUserWorkItem(new WaitCallback(ThreadMethod), autoEvent);//執行線程池 //在控制檯中顯示設置後的處於活動狀態的線程池請求的數目 Console.WriteLine("設置後,線程池中輔助線程的最大數爲:" + workerThreads.ToString() + ";線程池中異步I/O線程的最大數爲:" + portThreads.ToString()); Console.ReadLine(); } static void ThreadMethod(object stateInfo) { Console.WriteLine("執行線程池"); } } }
運行結果以下:
注意:在 .NET Framework 1.0 和 1.1 版中,不能從託管代碼中設置線程池大小。承載公共語言運行庫的代碼可使用 mscoree.h 中定義的 CorSetMaxThreads 設置該大小。
2.線程池的最小空閒線程數
即便是在全部線程都處於空閒狀態時,線程池也會維持最小的可用線程數,以便隊列任務能夠當即啓動。將終止超過此最小數目的空閒線程,以節省系統資源。默認狀況下,每一個處理器維持一個空閒線程。
在啓動新的空閒線程以前,線程池具備一個內置延遲(在 .NET Framework 2.0 版中爲半秒鐘)。應用程序在短時間內按期啓動許多任務時,空閒線程數的微小增長會致使吞吐量顯著增長。將空閒線程數設置得太高會浪費系統資源。
使用GetMinThreads和SetMinThreads方法能夠控制線程池所維持的空閒線程數。下面對這兩個方法進行一下說明。
(1)GetMinThreads方法
該方法用於檢索線程池在新請求預測中維護的空閒線程數。其語法以下:
public static void GetMinThreads(out int workerThreads,out int completionPortThreads)
參數說明:
workerThreads:Int32,當前由線程池維護的空閒輔助線程的最小數目。
CompletionPortThreads:Int32,當前由線程池維護的空閒異步I/O線程的最小數目。
(2)SetMinThreads方法
該方法用於設置線程池在新請求預測中維護的空閒線程數。其語法以下:
[SecurityPermissionAttribute(SecurityAction.Demand, ControlThread = true)] public static bool SetMinThreads(int workerThreads,int completionPortThreads)
參數說明:
workerThreads:類型:Int32,要由線程池維護的新的最小空閒輔助線程數。
CompletionPortThreads:Int32,要由線程池維護的新的最小空閒異步 I/O 線程數。
返回值:bool型,若是更改爲功,則爲 true;不然爲 false。
下面用一段代碼來詳細說明一下GetMinThreads和SetMinThreads方法的應用,代碼以下:
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading; namespace ConsoleApplication1 { class Program { static void Main(string[] args) { int minWorker, minIOC; //獲取線程池在新請求預測中維護的默認空閒線程數 ThreadPool.GetMinThreads(out minWorker, out minIOC); Console.WriteLine("設置前,線程池維護的空閒輔助線程的最小數目爲:" + minWorker.ToString() + ";線程池維護的空閒異步I/O線程的最小數目爲:" + minIOC.ToString());//在控制檯中顯示線程池的默認空閒線程數 minWorker = 4;//設置線程池維護的空閒輔助線程的最小數 minIOC = 10;//設置線程池維護的空閒異步I/O線程的最小數 if (ThreadPool.SetMinThreads(minWorker, minIOC))//若是更改爲功 { Console.WriteLine("設置後,線程池維護的空閒輔助線程的最小數目爲:" + minWorker.ToString() + ";線程池維護的空閒異步I/O線程的最小數目爲:" + minIOC.ToString());//在控制檯中顯示更改後的線程池的默認空閒線程數 } else { Console.WriteLine("沒有設置"); } Console.ReadLine(); } } }
運行結果以下:
注意:在.NET Framework 1.0版中,不能設置最小空閒線程數。