.NET简谈组件程序设计之(手动同步)
在上一篇文章“.NET簡(jiǎn)談組件程序設(shè)計(jì)之(上下文與同步域) ”中,我們學(xué)習(xí)了關(guān)于一些上下文和同步域的概念,可以利用這兩個(gè)技術(shù)來(lái)進(jìn)行自動(dòng)同步。
今天我們主要學(xué)習(xí)怎么手動(dòng)來(lái)執(zhí)行同步,能從更小的粒度進(jìn)行封鎖,以達(dá)到最大程度的吞吐量。[王清培版權(quán)所有,轉(zhuǎn)載請(qǐng)給出署名]
我們知道線程是進(jìn)程的運(yùn)行實(shí)體,進(jìn)程是資源分配單位,而線程是執(zhí)行單位。照書(shū)上所說(shuō),線程是程序的執(zhí)行路徑,當(dāng)我們分配一個(gè)線程的時(shí)候,要確定線程的執(zhí)行路徑是什么,也就是代碼中的ThreadStart委托所指向的入口點(diǎn)方法。
一旦我們手動(dòng)Start啟動(dòng)線程的時(shí)候,當(dāng)前上下文線程就被系統(tǒng)立即切換,我們從Thread.CurrentThread靜態(tài)屬性中可以準(zhǔn)確的獲取到當(dāng)前正在執(zhí)行的線程實(shí)例,然后進(jìn)行一系列的設(shè)置、等待、終止。[王清培版權(quán)所有,轉(zhuǎn)載請(qǐng)給出署名]
那么線程到底是怎么實(shí)現(xiàn)同步(互斥)的呢,在我們正常的代碼中是沒(méi)有關(guān)于線程同步的現(xiàn)實(shí)代碼的,所以在線程執(zhí)行路徑中是不存在任何能夠阻塞線程的實(shí)現(xiàn)代碼。要想實(shí)現(xiàn)線程阻塞,就必須在線程的執(zhí)行路徑中寫(xiě)點(diǎn)東西,讓所有線程當(dāng)進(jìn)入這段代碼的時(shí)候(也就是臨界資源),通過(guò)判斷某種東西來(lái)確定是否允許進(jìn)入執(zhí)行。
圖1:
在ThreadStartEnterPoint方法中,如果沒(méi)有任何的同步代碼,那么任何線程都能進(jìn)去執(zhí)行,就導(dǎo)致了亂七八糟的數(shù)據(jù)。當(dāng)數(shù)據(jù)在內(nèi)存中的時(shí)候,在同一時(shí)間只能是由CPU去執(zhí)行線程的代碼,但是線程是有競(jìng)爭(zhēng)情況的,當(dāng)線程1還沒(méi)有完全執(zhí)行完畢,線程2就來(lái)執(zhí)行這塊數(shù)據(jù),導(dǎo)致數(shù)據(jù)的不同步。
那么我們需要再線程1還沒(méi)有執(zhí)行完畢前不允許其他線程使用這塊內(nèi)存對(duì)象。當(dāng)線程1使用完后就立即釋放所占有的資源,讓其他線程能競(jìng)爭(zhēng)。
利用Monitor(監(jiān)視器)來(lái)進(jìn)行同步
Monitor是用來(lái)提供同步的對(duì)象,通過(guò)它可以在某個(gè)時(shí)間點(diǎn)上鎖定對(duì)象。請(qǐng)看代碼:
這是一個(gè)類中的兩個(gè)方法,在方法的頭部我用了MethodImpl方法特性進(jìn)行了標(biāo)識(shí),其實(shí)這個(gè)特性的目的就是在方法的入口處和結(jié)束處加上同步方法,也就是Monitor.Enter和Monitor.Exit,一般情況下我們都是習(xí)慣用lock來(lái)鎖定對(duì)象,其實(shí)lock也是Monitor的變體。這里我就不寫(xiě)出來(lái)了。讓我們熟悉一下陌生的使用方式。
在調(diào)用的代碼里面,大概意思是這樣的,我們同時(shí)開(kāi)啟兩個(gè)線程,入口點(diǎn)分別是上面的兩個(gè)方法,在PluseThread里面是為了將ShowMessage線程1從等待隊(duì)列里釋放出來(lái)繼續(xù)執(zhí)行。[王清培版權(quán)所有,轉(zhuǎn)載請(qǐng)給出署名]
在ShowMessage里面我用Monitor.Wait方法等待,當(dāng)調(diào)用這個(gè)方法的時(shí)候會(huì)使用我鎖定的對(duì)象,讓其他線程進(jìn)入執(zhí)行。當(dāng)Monitor.Pluse的時(shí)候,線程1繼續(xù)執(zhí)行。
靜態(tài)Monitor對(duì)象是每個(gè)線程都會(huì)執(zhí)行的路徑,我們通過(guò)控制Monitor來(lái)進(jìn)行線程同步,當(dāng)我們調(diào)用Wait就是等待,直到當(dāng)前對(duì)象Pluse才繼續(xù)執(zhí)行。
圖2:
利用WaitHandle(等待句柄)來(lái)進(jìn)行同步
上面我們通過(guò)Monitor來(lái)進(jìn)行同步,在同步的時(shí)候我們需要很好的控制等待時(shí)間,用Monitor也能通過(guò)Wait進(jìn)行等待超時(shí)設(shè)置,也許它內(nèi)部封裝是Windows等待句柄。
這里我們通過(guò)使用WaitHandle來(lái)進(jìn)行同步,WaitHandle是個(gè)抽象類,它的子類有很多,比如Mutex互斥體、ManualResetEvent、AutoResetEvent事件對(duì)象,等等。下面我們就來(lái)看看利用這些對(duì)象怎么同步線程。
Mutext互斥體
在方法的內(nèi)部我們申請(qǐng)一個(gè)Mutex對(duì)象,這個(gè)Mutex是全局的,就是在一臺(tái)機(jī)器上只能存在一個(gè)名稱的Mutex,Mutex可用來(lái)同步線程也可以用來(lái)同步進(jìn)程。
我們?cè)赑rint方法里面用WaitOne獲取句柄,如果已經(jīng)有線程“捷足先得”了,那么這里將阻塞,并返回false。
在使用完后,記得調(diào)用ReleaseMutex釋放當(dāng)前占用的Mutex句柄。
圖3:
利用ManualResetEvent(手動(dòng)事件)來(lái)進(jìn)行同步
我們直接看代碼吧,ManualResetEvent大家可能都用過(guò)。
這種類型的等待句柄對(duì)象是完全手動(dòng)控制的,讓我們想要用的時(shí)候要記得set,想要暫停的時(shí)候就Reset,不用了就close。
圖4:
利用AutoResetEvent(自動(dòng)事件)來(lái)進(jìn)行同步
從名字上就能看出,該事件是自動(dòng)重置事件,不需要想上面那樣進(jìn)行set\reset操作。
在上面的代碼中,我們通過(guò)WaitOne獲取等待句柄,當(dāng)我們獲取到之后,事件對(duì)象會(huì)自動(dòng)重置為信號(hào)已發(fā),其他線程無(wú)法獲取到等待句柄。當(dāng)我們set之后其他線程才能獲取到,這里省掉的是線程進(jìn)入執(zhí)行路徑的過(guò)程。
ManualResetEvent需要手動(dòng)進(jìn)行set才能使用,一旦set之后信號(hào)標(biāo)記為未發(fā)狀態(tài),所有線程都能執(zhí)行代碼,除非手動(dòng)Reset才能阻塞。
圖5:
[王清培版權(quán)所有,轉(zhuǎn)載請(qǐng)給出署名]
與50位技術(shù)專家面對(duì)面20年技術(shù)見(jiàn)證,附贈(zèng)技術(shù)全景圖總結(jié)
以上是生活随笔為你收集整理的.NET简谈组件程序设计之(手动同步)的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: 敏捷开发与中医理论系列之一:序言及为何中
- 下一篇: XML中的CDATA是什么