你好,我是阿輝。
前面2篇文章介紹了線程的基礎知識和線程同步,下面我們來一起認識學習下,線程池的使用。
線程池
創建線程是昂貴的操作,所以為每個短暫的異步操作創建線程會產生顯著的開銷。一般情況下,都會使用池,也就是線程池進行管理。
線程池可以成功地適應於任何需要大量短暫的開銷大的資源。事先分配一定的資源,將這些資源放入到資源池中。每次需要新的資源,只需從池中獲取一個,不需要創建新的,當該資源不再被使用時,就將其返回到池中。
在.NET中,線程池可以使用ThreadPool類型,受.NET通用語言運行時(CLR)管理。每個CLR都有一個線程池實例。ThreadPool類型擁有一個QueueUserWorkItem靜態方法。該方法接收一個委托,代表用戶自定義的一個異步操作。該方法被調用后,委托會進入到內部隊列中,如果線程池中沒有任何線程,將創建一個新的工作線程並將隊列中第一個委托放入到該工作線程中。
保持在線程中的操作都是短暫的是非常重要的。不要在線程池中放入長時間運行的操作,或者阻塞工作線程。 這將導致所有工作線程變的繁忙,從而無法服務用戶操作。這會導致性能問題和非常難以調式的錯誤。
在線程池中,如果停止向其放置新操作時,線程池最終會刪除一定時間后過期的不再使用的線程。這將釋放所有那些不再的系統資源。
線程池的用途是執行運行時間短的操作。使用線程池可以減少並行度耗費及節省操作系統資源。
線程池中的工作線程都是后台線程。這意味着當所有的前台線程(包括主線程)完成后,所有的后台線程將停止工作。
線程池中異步的使用
class Program
{
private delegate string RunOnThreadPool(out int threadId); //聲明委托
private static void Callback(IAsyncResult ar)
{
Console.WriteLine("觸發回調");
Console.WriteLine("異步狀態:" + ar.AsyncState);
Console.WriteLine("是否是線程池的線程:" + Thread.CurrentThread.IsThreadPoolThread);
Console.WriteLine("ThreadId:" + Thread.CurrentThread.ManagedThreadId);
}
private static string Test(out int threadId)
{
Console.WriteLine("Test開始");
Console.WriteLine("是否是線程池的線程:" + Thread.CurrentThread.IsThreadPoolThread);
Thread.Sleep(TimeSpan.FromSeconds(2));
threadId = Thread.CurrentThread.ManagedThreadId;
return "ThreadId:" + threadId;
}
static void Main(string[] args)
{
int threadId=0;
RunOnThreadPool poolDelegate=Test;
var t=new Thread(()=>Test(out threadId));
t.Start();
t.Join();
Console.WriteLine("Thread ID="+threadId);
IAsyncResult ar=poolDelegate.BeginInvoke(out threadId,Callback,"測試是否可以回調");
ar.AsyncWaitHandle.WaitOne();
string result=poolDelegate.EndInvoke(out threadId,ar);
Console.WriteLine("ID:"+threadId);
Console.WriteLine("結果:"+result);
Console.ReadKey();
}
}
執行后可以看到實際的顯示結果。
由於線程的構造函數只能接受一個無任何返回結果的方法,所以這里使用了lambda表達式來將對Test方法的調用包起來。
上面是一個很標准的在線程池中使用委托的例子,也可以學習到具體線程池的應用。可以看到當第一次線程池中沒有線程時,打印出來線程10不在線程中,當第二次在線程池中時,后面異步回調顯示出來的結果就是再次調用的線程11。
BeginInvoke方法接受一個回調函數,該回調函數會在異步操作完成后會被調用,並且一個用戶自定義的狀態會傳給該回調函數。該狀態通常用於區分異步調用,是一個實現了IAsyncResult接口的result對象。BeginInvoke立即返回結果,當線程池中的工作線程在執行異步操作時,仍允許繼續其他工作,可以通過result對象的IsCompleted屬性輪詢結果。當操作完成后,會得到一個結果,可以通過委托調用EndInvoke方法,將IAsyncResult對象傳遞給委托參數。
上面這句話其實主要是講解委托在線程池中的應用,如果你想得到某個線程的返回結果,就得使用這種異步委托來實現。
在線程池中使用委托時,調用EndInvoke方法是非常重要的。該方法會將任何未處理的異常拋回到調用線程中。當使用這種異步API時,請確保始終調用Begin和End方法。
上面使用的Begin/End方法和.NET中的IAsyncResult對象等方式被稱為異步編程模型(APM模式),這樣的方法叫異步方法。
線程池中還有一個有用的方法:ThreadPool.RegisterWaitForSingleObject。該方法允許我們將回調函數放入線程池中的隊列中。當提供的等待事件處理器收到信號或發生超時時,該回調函數將被調用。
在線程池中使用BackgroundWorker組件,可以顯示地指出后台工作線程支持取消操作及操作進度的通知。此時可以使用事件語法。
事件表示了一些通知的源或當通知到達時會有所響應的一系列訂閱者。
這種就是基於事件的異步模式(EAP),就是啟動一個異步操作然后訂閱給不同的事件,這些事件在該操作執行時會被觸發。
小寄語
人生短暫,我不想去追求自己看不見的,我只想抓住我能看的見的。
原創不易,給個關注。
我是阿輝,感謝您的閱讀,如果對你有幫助,麻煩點贊、轉發 謝謝。