C# Monitor類鎖定對(duì)象
C# Monitor類鎖定對(duì)象
當(dāng)多線程公用一個(gè)對(duì)象時(shí),也會(huì)出現(xiàn)和公用代碼類似的問題,這種問題就不應(yīng)該使用lock關(guān)鍵字了,這里需要用到System.Threading中的一個(gè)類Monitor,我們可以稱之為監(jiān)視器,Monitor提供了使線程共享資源的方案。
C# Monitor類可以鎖定對(duì)象,一個(gè)線程只有得到這把鎖才可以對(duì)該對(duì)象進(jìn)行操作。對(duì)象鎖機(jī)制保證了在可能引起混亂的情況下一個(gè)時(shí)刻只有一個(gè)線程可以訪問這個(gè)對(duì)象。
Monitor必須和一個(gè)具體的對(duì)象相關(guān)聯(lián),但是由于它是一個(gè)靜態(tài)的類,所以不能使用它來定義對(duì)象,而且它的所有方法都是靜態(tài)的,不能使用對(duì)象來引用。下面代碼說明了使用Monitor鎖定對(duì)象的情形:
- ……
- QueueoQueue=newQueue();
- ……
- Monitor.Enter(oQueue);
- ……
- //現(xiàn)在oQueue對(duì)象只能被當(dāng)前線程操縱了Monitor.Exit(oQueue);
- //釋放鎖
如上所示,當(dāng)一個(gè)線程調(diào)用Monitor.Enter()方法鎖定對(duì)象時(shí),這個(gè)對(duì)象就歸它所有了,其它線程想要訪問這個(gè)對(duì)象,只有等待它使用Monitor.Exit()方法釋放鎖。為了保證線程最終都能釋放鎖,你可以把Monitor.Exit()方法寫在try-catch-finally結(jié)構(gòu)中的finally代碼塊里。
對(duì)于任何一個(gè)被Monitor鎖定對(duì)象,內(nèi)存中都保存著與它相關(guān)的一些信息:其一是現(xiàn)在持有鎖的線程的引用;其二是一個(gè)預(yù)備隊(duì)列,隊(duì)列中保存了已經(jīng)準(zhǔn)備好獲取鎖的線程;其三是一個(gè)等待隊(duì)列,隊(duì)列中保存著當(dāng)前正在等待這個(gè)對(duì)象狀態(tài)改變的隊(duì)列的引用。
當(dāng)擁有對(duì)象鎖的線程準(zhǔn)備釋放鎖時(shí),它使用Monitor.Pulse()方法通知等待隊(duì)列中的***個(gè)線程,于是該線程被轉(zhuǎn)移到預(yù)備隊(duì)列中,當(dāng)對(duì)象鎖被釋放時(shí),在預(yù)備隊(duì)列中的線程可以立即獲得對(duì)象鎖。
下面是一個(gè)展示如何使用lock關(guān)鍵字和C# Monitor類來實(shí)現(xiàn)線程的同步和通訊的例子,也是一個(gè)典型的生產(chǎn)者與消費(fèi)者問題。
這個(gè)例程中,生產(chǎn)者線程和消費(fèi)者線程是交替進(jìn)行的,生產(chǎn)者寫入一個(gè)數(shù),消費(fèi)者立即讀取并且顯示(注釋中介紹了該程序的精要所在)。
用到的系統(tǒng)命名空間如下:using System;using System.Threading;
首先,定義一個(gè)被操作的對(duì)象的類Cell,在這個(gè)類里,有兩個(gè)方法:ReadFromCell()和WriteToCell.消費(fèi)者線程將調(diào)用 ReadFromCell()讀取cellContents的內(nèi)容并且顯示出來,生產(chǎn)者進(jìn)程將調(diào)用WriteToCell()方法向 cellContents寫入數(shù)據(jù)。
示例如下:
- public class Cell
- {
- int cellContents; // Cell對(duì)象里邊的內(nèi)容
- bool readerFlag = false;
- // 狀態(tài)標(biāo)志,為true時(shí)可以讀取,為false則正在寫入
- public int ReadFromCell( )
- {
- lock(this) // Lock關(guān)鍵字保證了什么,請(qǐng)大家看前面對(duì)lock的介紹
- {
- if (!readerFlag)//如果現(xiàn)在不可讀取
- {
- try
- {
- //等待WriteToCell方法中調(diào)用Monitor.Pulse()方法
- Monitor.Wait(this);
- }
- catch (SynchronizationLockException e)
- {
- Console.WriteLine(e);
- }
- catch (ThreadInterruptedException e)
- {
- Console.WriteLine(e);
- }
- }
- Console.WriteLine("Consume: {0}",cellContents);
- readerFlag = false;
- //重置readerFlag標(biāo)志,表示消費(fèi)行為已經(jīng)完成
- Monitor.Pulse(this);
- //通知WriteToCell()方法(該方法在另外一個(gè)線程中執(zhí)行,等待中)
- }
- return cellContents;
- }
- public void WriteToCell(int n)
- {
- lock(this)
- {
- if (readerFlag)
- {
- try
- {
- Monitor.Wait(this);
- }
- catch (SynchronizationLockException e)
- {
- //當(dāng)同步方法(指Monitor類除Enter之外的方法)在非同步的代碼區(qū)被調(diào)用
- Console.WriteLine(e);
- }
- catch (ThreadInterruptedException e)
- {
- //當(dāng)線程在等待狀態(tài)的時(shí)候中止
- Console.WriteLine(e);
- }
- }
- cellContents = n;
- Console.WriteLine("Produce: {0}",cellContents);
- readerFlag = true;
- Monitor.Pulse(this);
- //通知另外一個(gè)線程中正在等待的ReadFromCell()方法
- }
- }
- }
【編輯推薦】