異步、多線程、任務、並行編程:選擇合適的多線程模型

 

本篇概述:算法

@FCL4.0中已經存在的線程模型,以及它們之間異同點;編程

@多線程編程模型的選擇。

 多線程

1:異步、多線程、任務、並行的本質異步

這四個概念對應在CLR中的本質,本質都是多線程。學習

異步,簡單的講就是BeginInvoke、EndInvoke模式,它在CLR內部線程池進行管理;優化

多線程,體如今C#中,能夠由類型Thread發起。也能夠由ThreadPool發起。前者不受CLR線程池管理,後者則是。FCL團隊爲了各類編程模型的方便,還另外提供了BackgroundWorker和若干個Timer,基本上它們都是ThreadPool的增強,增長了一些和調用者線程的交互功能;spa

任務(Task),爲FCL4.0新增的功能,在一個稱之爲任務並行庫(TPL)的地方,其實也就是System.Threading.Tasks命名空間下。任務並行庫名字取的很玄乎,其實它也是CLR線程池的增強。優化了線程間的調度算法,增長了和調用者線程的交互功能;線程

並行(Parallel),爲FCL4.0新增的功能,也屬於TPL。並行在後臺使用Task進行管理,說白了,由於Task使用的線程池線程,因此Parallel天然使用的也是線程池線程進行管理,它的本質僅僅是進一步簡化了Task。在這裏要增進一個對於並行的理解。實際上,多線程自然就是並行的。及時不用任務並行庫,用Thread類型新起兩個線程,CLR或者說Windows系統也會將這兩個線程根據須要安排到兩個CPU上去執行。因此,並非由於多了任務並行庫,CLR才支持並行計算,任務並行庫只是提供了一組API,使咱們可以更好的操縱線程進行並行開發而已。

2:遺憾對象

Jeffrey Richter大叔說,微軟提供了這麼多線程模型,是遺憾的,由於這製造了混亂。不少開發者都不知道該選用哪一個類型來編寫本身的多線程代碼。咱們對微軟老是又愛又恨,它老是不停的更新一些東西,逼迫咱們不停的學習。可是也好,進步致使它不會過早死掉,讓咱們完全失掉飯碗。
C#剛出來的被人笑,如今它的不少語法特性已經比Java優美。不少時候咱們太擅長於嘲笑,以至最後只能哭。順便說一句,我依然是那麼的喜歡JAVA,只是好久沒用它而已。

3:如今,該用什麼來編寫多線程 內存

若是你在FRAMEWORK4.0下編寫代碼,那麼應該按照這個優先級來撰寫多線程代碼: 

優先

次優先

不得以

Parallel(含擴展庫PLinq)

Task

ThreadPool(BackgroundWorker,Timer)

異步

Thread

這個表知足了大部分狀況下的一個優先級指導,但在某些狀況下會有例外。

3.1:爲何 Parallel和Task優先級同樣,而不是優於Task?

Parallel雖然在後臺使用Task進行管理,而且它所謂簡化了對於Task的操做,可是它有一個重要的特徵區別與Task:Parallel會阻滯調用者線程。查看Paralle的成員,有For、ForEach、Invoke方法,它甚至都沒提供一個BeginInvoke方法,也很好的暗示了這一點。不過雖然是同步的執行的,Parallel仍是會把多個任務分配到多個CPU上去。
Task被用的最多的是Start方法,它不會阻滯主線程。雖然Task也提供了同步的啓動線程的方法RunSynchronously,但通常用的很少。 

3.2:什麼時候用異步,什麼時候用線程或線程池

這須要從「IO操做的DMA(Direct Memory Access)模式」講起。經過DMA的數據交換幾乎能夠不損耗CPU的資源。在硬件部分,硬盤、網卡、聲卡、顯卡等都有DMA功能。能夠簡單的認爲,當咱們的工做線程須要操做I/O資源的時候(如讀取一個大文件、讀取一個網頁、讀取Socke包等),咱們就須要用異步去作這些事情。異步模式只會在工做開始以及工做結束的時候佔用CLR線程池,其它時候由硬盤、網卡等硬件設備來處理具體的工做,這就不會過多佔用到CPU空間和時間損耗。 

歸納而言:

計算密集型工做,直接採用線程;

IO密集型工做,採用異步機制;

當咱們不清楚什麼工做是I/O密集型的,一個不是很恰當的指導就是:查看FCL類型成員,若是成員提供了相似BeginDosomething方法的,則優先使用它,而不是新起一個線程或丟到線程池。

3.3:線程池的優點

新起線程,會帶來很大的開銷,這些開銷主要集中在:分配線程內核對象、線程環境塊、用戶模式棧、內核模式棧所須要的內存空間,加載的DLL的DLLMain方法,並傳遞鏈接標誌,以及線程上下文切換。因爲線程如此昂貴,因此對於普通的開發要求來講,線程池就是一個很好的選擇。線程池替開發人員管理工做線程,當一項工做完畢的時候,CLR不會銷燬這個線程,而是會保留這個線程一段時間,看是否有別的工做須要這個線程。至於什麼時候銷燬或新起線程,由CLR決定。


3.4:什麼時候用Thread 
以上的各類線程模型,它們最終都是Thread。 那麼何時須要Thread直接出場呢?

最重要的使用Thread的理由是,咱們須要控制線程的優先級。Thread之上的線程模型都不支持優先級設置。設置一個線程的高優先級可使它得到更多的CPU時間;

再者,能夠控制線程爲前臺線程。固然,由Thread新起的線程默認就是前臺線程。前臺線程不隨着調用者線程的中斷而中斷,這使得咱們能夠用Thread來進行一些關鍵性的操做。

相關文章
相關標籤/搜索