線程池System.Threading.ThreadPool,可用於發送工作項目、處理非同步I/O、代表其它線程等待以及處理計時器。基本用法:
public void Main() { ThreadPool.QueueUserWorkItem(JobForAThread); // 將某工作交給線程池}void JobForAThread(object state) // 線程要執行的工作:滿足 委託WaitCallback {for (int i = 1; i <= 5; i++) { Console.WriteLine("Running Thread {0},Step {1}", Thread.CurrentThread.ManagedThreadId, i); Thread.Sleep(500); } }
在執行 ThreadPool.QueueUserWorkItem() 方法後,處理器就會自動在池中選擇一個線程來處理“工作內容”。
1.如果線程池還沒有運行,就會建立一個線程池,並啟動第一個線程。
2.如果線程池已經在運行,且至少有一個閒置線程,線程池就會把改“工作內容”交給這個閒置線程來處理。
3.如果當時線程池沒有閒置線程,該工作就會處於等待狀態,直到有空閑線程來處理它。
通過 ThreadPool.GetMaxThreads() 方法來 檢索可以同時處於活動狀態的線程池請求的數目。
int vWorkerThreads; int vCompletionPortThreads; ThreadPool.GetMaxThreads(out vWorkerThreads, out vCompletionPortThreads); Console.WriteLine("池中輔助線程的最大數{0},池中非同步 I/O 線程的最大數{1}", vWorkerThreads, vCompletionPortThreads);
可以通過 ThreadPool.SetMaxThreads() 方法設定可以同時處於活動狀態的線程池請求的數目。
ThreadPool.SetMaxThreads(5, 4);
但是,不能將輔助線程的數目或非同步I/O完成線程的數目設定位 小於 電腦處理器的數目。線程池使用很簡單,但又一些限制:
1.線程池中的所有線程都是後台線程。如果進程的所有前台線程都結束了,所有後台線程就會停止。不能把入池的線程改為前台線程。
2.不能給入池的線程設定優先權或名稱。
3.入池的線程只能用於時間較短的任務。如果線程要一直運行,就應該使用Thread類建立一個線程。
給 JobForAThread() 工作任務傳遞參數 object state,調用:
public void Main() { ThreadPool.QueueUserWorkItem(JobForAThread,"這是傳遞給工作內容的參數"); // 添加工作的同時,傳遞參數Console.ReadKey(); // 讓主線程等待,否則“一閃而過” }void JobForAThread(object state) { Console.WriteLine("收到資訊:{0}", (string)state); // 處理傳進來的資料for (int i = 1; i <= 5; i++) { Console.WriteLine("Running Thread {0},Step {1}", Thread.CurrentThread.ManagedThreadId, i); Thread.Sleep(500); } }
簡單的控制操作
一般情況下,“工作”交給線程池後,就不受控制了, 它會由處理器自動決定什麼時候開始執行(當然是有空閑線程才行)。可以通過以下代碼,讓工作在指定時候以後再開始執行。
ManualResetEvent mManualEvent;public void Main() { mManualEvent = new ManualResetEvent(false); // 執行個體ThreadPool.QueueUserWorkItem(JobForAThread); Console.WriteLine("{0} 任務已經交給線程池了,但是它沒有執行.", DateTime.Now.ToString("HH:mm:ss")); Thread.Sleep(10000); // 等待 10smManualEvent.Set(); // 發出訊號,讓線程繼續執行 Console.ReadKey(); // 讓主線程等待,否則“一閃而過” }void JobForAThread(object state) { mManualEvent.WaitOne(); // 等待 “ mManualEvent.Set();” 這一句執行(發送訊號)Console.WriteLine("{0} 現在開始執行任務啦.", DateTime.Now.ToString("HH:mm:ss"));for (int i = 1; i <= 5; i++) { Console.WriteLine("Running Thread {0},Step {1}", Thread.CurrentThread.ManagedThreadId, i); Thread.Sleep(500); } }
運行結果:
這裡在將工作交給線程池後,線程執行工作時,一直阻塞在 mManualEvent.WaitOne(); 這一句,直到10s後主線程發出了訊號,該工作才繼續執行後續代碼。這是一種“假開始”控制操作,本質上並沒有實現讓指定工作在希望的時候開始工作。這裡 在初始化 ManualResetEvent 對象時參數 false 表示,預設將訊號置為“阻塞狀態”,通過代碼 mManualEvent.Set(); 將訊號置為“可繼續狀態”。反之,可以通過代碼 mManualEvent.Reset(); 將線程置為“阻塞狀態”。
有時,需要等待所有線程池中的線程執行完成後,才繼續執行其它某些代碼。
AutoResetEvent[] mAutoResetEvent;public void Main() { mAutoResetEvent = new AutoResetEvent[]{new AutoResetEvent(false), // 預設訊號為 阻塞new AutoResetEvent(false),new AutoResetEvent(false) };for (int i = 0; i < 3; i++) // 建立3個工作 { Thread.Sleep(1000); ThreadPool.QueueUserWorkItem(JobForAThread, i); } Console.WriteLine("所有工作已經添加到池中..."); WaitHandle.WaitAll(mAutoResetEvent); // 等待 mAutoResetEvent 中所有訊號變為 繼續 後,繼續後面代碼Console.WriteLine("所有工作已經完成了"); Console.ReadKey(); // 讓主線程等待,否則“一閃而過” }void JobForAThread(object state) {int vJobIndex = (int)state; Console.WriteLine("Job {0} Started.", vJobIndex);for (int i = 1; i <= 5; i++) { Console.WriteLine("Running Thread {0},Step {1},Job {2} ", Thread.CurrentThread.ManagedThreadId, i, vJobIndex); Thread.Sleep(500); } mAutoResetEvent[vJobIndex].Set(); }
運行結果:
[]