上一章我們了解到,由於線程的創建,銷毀都是需要耗費大量資源和時間的,開發者應該非常節約的使用線程資源。最好的辦法是使用線程池,線程池能夠避免當前進行中大量的線程導致操作系統不停的進行線程切換,當線程數量到達了我們設置的上限,線程會自動排隊等待,當線程資源可用時,隊列中的線程任務會依次執行,如果沒有排隊等候的資源,線程會變為閒置狀態。
這種做法可以讓我們不用那麼復雜的去實現創建,重用線程的邏輯,但是也有一些限制,比如由他內置的方法,我們不知道什麼時候線程池裡面的任務會結束,也不能獲取線程的返回值。為了解決這些問題,微軟引入了一個新的概念。
引入了Task之後,你可以用如下實現來替代ThreadPool
這些實現都是等價的。Task本身實現了很多ThreadPool不能做的事情。
更多Task同步編程的使用,請參見(還沒寫,先給自己挖個坑O(∩_∩)O)。
ThreadPool.QueueUserWorkItem沒有提供一種簡單的機制來獲取線程的返回值。異步委托解決了這個問題,支持了傳入一系列的參數。此外,異步委托中沒有處理的異常會很方便的在調用線程的重新拋出(在調用EndInvoke的時候),因此不需要顯示的處理。
通過異步委托來執行任務主要分一下幾步:
調用BeginInvoke不會阻塞當前線程,因此你可以在調用完之後執行其他你想要同步的操作
EndInvoke主要做3件事: 1. 等待異步委托完成 2. 接收返回值 3. 把異步線程中未處理的異常在當前線程中重新拋出。
你也可以在調用BeginInvoke的時候指定一個回調方法,這個方法會在異步委托結束的時候自動調用。這樣異步委托就像是一個後台線程一樣自動執行,不需要主線程等待。只需要在BeginInvoke的時候做一些額外的操作即可實現這種操作。
Jeffery在C# via CLR Chapter27中針對線程池的使用給出了一些建議。目前我們允許開發者來指定一個線程池的最大線程數。但是事實證明,我們往往不應該為一個線程池指定線程的上限,否則可能會出現程序死鎖或者餓死的狀態。比如你可能設置了1000個線程,但是某一時刻正好有第1001個線程需要等待所有線程結束才能執行,這種情況如果你限制了線程池線程的個數,就會出現死鎖。從開發的另一個角度說,你也不應該限制一個進程使用多少資源,比如一個進程可以使用多少內存,使用多少帶寬.因此雖然目前你可以通過GetMaxThreads, SetMaxThreads,GetMinThreads,SetMinThreads ,GetAvailableThreads來進行線程個數的限制,但是他仍然不建議大家這樣做。這些限制可能會讓你的程序運行的更慢。