本篇文章涵蓋一下幾部分內(nèi)容: 1. 什么是異步編程,為什么會需要異步編程 2. .NET下的異步編程及其發(fā)展 3. .NET線程同步機(jī)制及線程間數(shù)據(jù)封送 4. 異步模式 5. 線程安全及異常處理 6. 線程取消 什么是異步編程,為什么會需要異步編程 這個世界上資源是受限的。但資源限制和懶惰一樣促進(jìn)了工業(yè)和科技的發(fā)展。在計(jì)算機(jī)方面舉個例子,計(jì)算機(jī)非得是二進(jìn)制嗎?對計(jì)算機(jī)來說二進(jìn)制最好嗎?不是,這是由于當(dāng)時工業(yè)水平限制,把電壓分成兩份表示0和1比分成三份更加方便且可靠;虛擬內(nèi)存管理,Cache等技術(shù)都是由當(dāng)時硬件條件所限逼出來的技術(shù),同樣異步編程和分布式編程也是。生活中的好多事物都不是線性的,拿學(xué)生時代的一個常見的例子說一下,明天開學(xué),海量作業(yè)一點(diǎn)沒寫,于是找個同學(xué)作業(yè)抄一下,但在短時間內(nèi)一個人很難抄得完,于是我花錢請了幾個同學(xué)一起抄,把一份工作分給幾個人去做,這就是異步了。但除去筆跡不同這么做沒有一個人抄安全,有可能哥幾個把一份內(nèi)容重復(fù)抄了好幾遍(線程安全),這期間萬一筆,紙,橡皮沒準(zhǔn)備充分還得有一個資源爭用,死鎖的問題(同步的問題),哥幾個抄得時候還會相互報(bào)一下各自進(jìn)度(線程間數(shù)據(jù)封送),所以說這么干是有風(fēng)險(xiǎn)的我們就得有個機(jī)制避免這種風(fēng)險(xiǎn)的發(fā)生,異步編程和這個類似。 那在編程中異步會用在什么地方呢?一個簡單情形,圖形界面程序,后臺如果要連接數(shù)據(jù)庫查詢或?qū)懭牒A繑?shù)據(jù)或者進(jìn)行I/O操作,界面會“假死”。之所以發(fā)生這種情況是這些處理都在UI線程中,這些操作占用UI線程時,任何拖動UI,點(diǎn)擊按鈕等操作都得不到及時響應(yīng)。解決的方法是將這些需要長時間的操作放入一個新的線程異步操作,把UI線程解放出來。其它的應(yīng)用比如海量數(shù)據(jù)計(jì)算,服務(wù)器響應(yīng)客戶端請求等等。 .NET下的異步編程及其發(fā)展 首先說明一點(diǎn),線程可以分為前臺線程和后臺線程。前臺線程和吸血鬼差不多很恐怖,要想干死進(jìn)程,就必須把所有的前臺進(jìn)程都干掉,UI線程就是前臺線程。而后臺線程就是二房生的兒子了,進(jìn)程消亡后緊跟著死掉了,很明顯的后臺線程就是Word的拼寫檢查,或者outlook負(fù)責(zé)跟服務(wù)器同步更新郵件的線程。 任何平臺和編程語言都會有多線程的實(shí)現(xiàn)機(jī)制和方法。對于C#來講Thread類就是創(chuàng)建線程,管理線程的一種最初始的手段。但是創(chuàng)建和銷毀一個線程是很耗費(fèi)資源的,而且創(chuàng)建的線程越多,線程間切換就越頻繁(計(jì)算機(jī)CPU個數(shù)受限),線程切換也要耗費(fèi)資源和時間,再加上線程管理是一件很費(fèi)心的事,所以微軟就引入了線程池的概念。線程池是一個先進(jìn)先出FIFO的隊(duì)列,程序員只需要把操作或者任務(wù)丟給線程池,讓.NET framework替程序員管理線程,線程復(fù)用等,極大的簡化了開發(fā)。這里就有一個控制線程池內(nèi)線程數(shù)量的問題。線程池內(nèi)的線程肯定得根據(jù)需要動態(tài)變化,但適應(yīng)這種需要的算法是什么呢? 一個簡單的算法:往線程池中增加一些線程,觀察線程池的吞吐量,如果增加后吞吐量增加,說明線程不夠,需要增加線程。但這存在一個問題,對于一個很大的任務(wù)需要長時間占用線程,增加線程并不能增加吞吐量,此時如果增加線程會加重負(fù)擔(dān)。所以在CLR v4時引入了本地隊(duì)列(Local Queue)的概念,如果一個線程內(nèi)創(chuàng)建了另一個線程,新創(chuàng)建的線程不再丟給全局隊(duì)列,而是給本地隊(duì)列排隊(duì)等候調(diào)用。這就又有個問題,如果一個隊(duì)列內(nèi)任務(wù)執(zhí)行完了,而另一個隊(duì)列還有好多怎么辦?那就讓執(zhí)行完任務(wù)的本地隊(duì)列從該隊(duì)列上“偷“一個線程執(zhí)行。這樣達(dá)到負(fù)載均衡。當(dāng)然線程池的算法會隨著CLR版本升級而不斷演進(jìn),更加智能的管理線程。對普通開發(fā)者而言可以不用考慮這些細(xì)節(jié),無縫的體驗(yàn)線程池帶來的便利和效率就行了。 線程池如此方便,我們怎么使用線程池呢?可以通過以下幾種方式: 通過類方法ThreadPool.QueueUserWorkItem直接調(diào)用。 通過.net Framework 4.0 引入的TPL(Task Parallel Library)任務(wù)并行庫。 TPL中最主要的兩個類是Task和Parallel。而新版C++標(biāo)準(zhǔn)中也引入了類似的概念parallel_for, parallel_foreach, parallel_invoke等。 詳細(xì)信息見以下鏈接。 通過異步委托(BeginInvoke/EndInvoke)調(diào)用。 通過BackgroundWorker, BackgroundWorker是WinForm, WPF下的一個控件,主要用于提供UI控件下的協(xié)作式取消,進(jìn)度報(bào)告等。 這里我還要講一下PFX(Parallel FrameWork)。PFX從概念上可以分為數(shù)據(jù)并行和任務(wù)并行。 上層的由兩個結(jié)構(gòu)化數(shù)據(jù)并行APIs組成:PLINQ和Parallel類。而底層的任務(wù)并行包含了Task 類和一系列的附屬結(jié)構(gòu)用于幫助并行編程。注意PFX是建立在線程池之上的,是更好使用線程池的一種途徑,有說法說是用TPL比直接使用線程池效率更高。關(guān)于PLINQ,Task,Parallel類及上圖所列結(jié)構(gòu)的使用請參考一下鏈接。 .NET線程同步機(jī)制及線程間數(shù)據(jù)封送 首先.Net的同步機(jī)制是干什么的?概況來講是為了安全。同步機(jī)制的存在是因?yàn)楫惒讲僮魇遣话踩?,會帶來一系列的問題,這些問題在第一章節(jié)中已經(jīng)討論過了。而線程間數(shù)據(jù)封送和COM與.Net framework數(shù)據(jù)封送一樣,是為了線程間數(shù)據(jù)和狀態(tài)的傳遞。 那么.net的同步機(jī)制有哪些呢?概括一下:
lock(Monitor.Enter/Monitor.Exit):首先強(qiáng)調(diào)一下它不可以跨進(jìn)程間線程同步。一般跨進(jìn)間線程同步都有一個特征,就是同步對象都有名字。 Mutex和Semophore(slim):這兩個都可以跨進(jìn)程同步,兩者的區(qū)別在于:Mutex只能有一等待資源,而Semophore可以有多個。拿廁所舉例,Mutex相當(dāng)于廁所中只有一個蹲位,只能一個上了才能上另一個,而Semophore可以有多個蹲位,可以讓多個線程同時阻塞一個線程的執(zhí)行。就是n個哥們一起蹲著,又來一哥們,然后這n個哥們就占著那啥不那啥。 Reade/Writer 鎖。 3.基于信號 事件等待句柄AutoResetEvent, ManualResetEvent(Slim):注意這兩個也是允許跨進(jìn)程的,兩者用法差不多,使一個線程釋放一個信號從而使得其他線程能夠執(zhí)行。 CountdownEvent(4.0被引入):這個和上邊用法正好相反,它使得一個線程等待收到其他線程的信號后再執(zhí)行。 Barrier Wait and pulse 4. 非阻塞的同步結(jié)構(gòu) Thread.MemoryBarrier Thread.VolatileRead/Write Interlocaked 關(guān)于以上同步機(jī)制具體應(yīng)用和Demo代碼請參考以下鏈接。 而關(guān)于線程間數(shù)據(jù)封送,一個很好的例子就是點(diǎn)擊button后開始在新線程中執(zhí)行某個操作,但執(zhí)行過程需要在一個label上顯示出來,這時候就需要把新線程內(nèi)表示執(zhí)行狀態(tài)的數(shù)據(jù)對象封送回UI線程。這部分內(nèi)容可以參考我以前寫的一篇帖子:http://www.cnblogs.com/salomon/archive/2012/06/28/2567339.html。 異步模式 什么需要異步模式?所謂模式,其實(shí)是一種方法,就跟上篇博客里所講的,是從工程實(shí)踐中總結(jié)出來的解決相似或特定問題的一種慣用手段。常見的異步模式包括: APM模式: BeginXXX/EndXXX, IAsyncResult EAP模式(基于事件的異步模式) Windows Form MethodNameAsync Event TAP(基于任務(wù)的異步模式) MethodNameAsync Task/Task<Result> 這部分內(nèi)容以下鏈接講得很好了,感興趣可以看一下。更詳盡的介紹去MSDN或者官方網(wǎng)站上去找相似的文檔。 線程安全及異常處理 新線程中拋出的異常會不會自動封送到主線程中?如何處理新線程中拋出的異常?什么是線程安全?怎樣做到線程安全? 線程取消 正在執(zhí)行的線程怎么能不能取消,怎么取消合適?暴力取消?協(xié)作式取消? C#5.0新的異步模式Async和await關(guān)鍵字 請參考我以前的博客:http://www.cnblogs.com/salomon/archive/2012/06/27/2565862.html 原文鏈接:http://www.cnblogs.com/salomon/archive/2012/07/26/2610548.html
【編輯推薦】
|
|