ThreadPool(線程池) in .Net
http://rickie.cnblogs.com/archive/2004/11/23/67275.html
在多線程的程序中,經常會出現兩種情況。一種情況下,應用程序中的線程把大部分的時間花費在等待狀態,等待某個事件發生,然後才能給予響應;而另外一種情況則是線程平常都處於休眠狀態,只是週期性地被喚醒。這裏分析及介紹.Net Framework中ThreadPool class來對付第一種情況,相應地也會談到QueueUserWorkItem方法和WaitCallback委託。而使用Timer(System.Threading.Timer or System.Windows.Forms.Timer)來對付第二種情況,可以參考《System.Threading.Timer類的TimerCallback 委託》。
1. ThreadPool介紹(From MSDN)
ThreadPool class提供了一個線程池,該線程池可用於發送工作項、處理異步 I/O、代表其他線程等待以及處理計時器。線程池允許在後臺運行多個工作,而不需要爲每個任務頻繁地創建和銷燬單獨的線程,從而減少了開銷。
線程池通過爲應用程序提供一個由系統管理的輔助線程池使您可以更爲有效地使用線程。一個線程監視排到線程池的若干個等待操作的狀態。當一個等待操作完成時,線程池中的一個輔助線程就會執行對應的回調函數。
也可以將與等待操作不相關的工作項排列到線程池。若要請求由線程池中的一個線程來處理工作項,請調用 QueueUserWorkItem 方法。此方法將對將被從線程池中選定的線程調用的方法或委託的引用用作參數。一個工作項排入隊列後就無法再取消它。
計時器(System.Threading.Timer)隊列中的計時器以及已註冊的等待操作也使用線程池。它們的回調函數也會排列到線程池。關於這部分的內容,可以參考《System.Threading.Timer類的TimerCallback 委託》。
線程池在首次創建 ThreadPool 類的實例時被創建。線程池具有每個可用處理器 25 個線程的默認限制,這可以使用 mscoree.h 文件中定義的 CorSetMaxThreads 來更改。每個線程使用默認的堆棧大小並按照默認的優先級運行。每個進程只能具有一個操作系統線程池。
2. ThreadPool.QueueUserWorkItem方法
QueueUserWorkItem方法將指定的方法排入隊列以便執行,並指定包含該方法所用數據的對象,此方法在有線程池線程變得可用時執行。
public static bool QueueUserWorkItem(
WaitCallback callBack,
object state
);
如果將方法成功排入隊列,則爲 true;否則爲 false。如果排入隊列的方法僅需要單個數據項,可以將數據項強制轉換爲類型 Object。如果該方法需要多個複雜數據,則必須定義包含這些數據的類。如果沒有參數傳入,則可以調用QueueUserWorkItem(WaitCallback callBack)重載。
ThreadPool提供的公共方法都是static方法,因此也不需要生成ThreadPool對象。通過QueueUserWorkItem方法在線程池中添加一個工作項目後,目前沒有提供簡單的方法取消。你不必建立諮監線程,只需要把相應的函數或方法依託WaitCallback委託傳遞給ThreadPool.QueueUserWorkItem()方法即可。而線程的創建、管理和運行等等都由系統自動完成,這就是ThreadPool的優點。
3. WaitCallback委託
WaitCallback委託聲明線程池要執行的回調方法,回調方法的聲明必須與WaitCallback委託聲明具有相同的參數。
WaitCallback 表示要在 ThreadPool 線程上執行的回調方法。創建委託,方法是將回調方法傳遞給 WaitCallback 構造函數。您的方法必須具有此處所顯示的簽名。通過將WaitCallback 委託傳遞給 ThreadPool.QueueUserWorkItem 來將任務排入隊列以便執行。您的回調方法將在某個線程池線程可用時執行。
如果要將信息傳遞給回調方法,請創建包含所需信息的對象,並在將任務排入隊列以便執行時將它傳遞給 QueueUserWorkItem。每次執行您的回調方法時,state 參數都包含此對象。
通過將一個方法打包到 WaitCallback 委託中,然後將該委託傳遞給 ThreadPool.QueueUserWorkItem 靜態方法,在線程池中對任務進行排隊。
4. Demo application using ThreadPool class
codeproject.com上有一個不錯的Demo: Proper Threading in Winforms .Net, written by Shawn Cicoria,並附有Source code。
在上述Demo程序中,不僅提供了使用ThreadPool線程池的方法,而且還演示了Thread和ThreadStart 委託的方法。
private void button1_Click(object sender, System.EventArgs e)
{
ShowProgressDelegate showProgress = new ShowProgressDelegate(ShowProgress);
int imsgs = 100;
//One Way... Using ThreadPool
if ( cbThreadPool.Checked )
{
object obj = new object[] { this, showProgress, imsgs };
WorkerClass wc = new WorkerClass();
bool rc = ThreadPool.QueueUserWorkItem( new WaitCallback (wc.RunProcess), obj);
EnableButton( ! rc );
}
else
{
//another way.. using straight threads
WorkerClass wc = new WorkerClass( this, showProgress, new object[] { imsgs } );
Thread t = new Thread( new ThreadStart(wc.RunProcess));
t.IsBackground = true; //make them a daemon - prevent thread callback issues
t.Start();
EnableButton ( false );
}
}
另外,上述Demo程序中還調用Windows form控件的BeginInvoke方法:在創建控件的基礎句柄所在線程上,用指定的參數異步執行指定委託。BeginInvoke方法在不同的線程池線程上回調指定的委託,實際上就是發起後臺的調用來放置一條消息到windows form的消息循環(message loop)中。
public IAsyncResult BeginInvoke(Delegate, object[]);
***
Any questions about the demo application, please leave message below. I will try to explain it. Thanks.
References:
1, MSDN, ThreadPool, QueueUserWorkItem and WaitCallback
2, Shawn Cicoria, Proper Threading in Winforms .Net, http://www.codeproject.com/csharp/winformthreading.asp