天天看點

異步、多線程、任務、并行程式設計之一:選擇合适的多線程模型

異步、多線程、任務、并行程式設計之一:選擇合适的多線程模型

本篇概述:

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

@多線程程式設計模型的選擇。

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

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

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

多線程,展現在C#中,可以由類型Thread發起。也可以由ThreadPool發起。前者不受CLR線程池管理,後者則是。FCL團隊為了各種程式設計模型的友善,還另外提供了BackgroundWorker和若幹個Timer,基本上它們都是ThreadPool的加強,增加了一些和調用者線程的互動功能;

任務(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來進行一些關鍵性的操作。

異步、多線程、任務、并行程式設計之一:選擇合适的多線程模型

本文基于

Creative Commons Attribution 2.5 China Mainland License

釋出,歡迎轉載,演繹或用于商業目的,但是必須保留本文的署名

http://www.cnblogs.com/luminji

(包含連結)。如您有任何疑問或者授權方面的協商,請給我留言。