摘要:本文介紹C# Monitor類可以鎖定對象,一個線程只有得到這把鎖才可以對該對象進行操作。對象鎖機制保證了在可能引起混亂的情況下一個時刻只有一個線程可以訪問這個對象。
當多線程公用一個對象時,也會出現和公用代碼類似的問題,這種問題就不應該使用lock關鍵字了,這裡需要用到System.Threading中的一個類Monitor,我們可以稱之為監視器,Monitor提供了使線程共用資源的方案。
C# Monitor類可以鎖定對象,一個線程只有得到這把鎖才可以對該對象進行操作。對象鎖機制保證了在可能引起混亂的情況下一個時刻只有一個線程可以訪問這個對象。
Monitor必須和一個具體的對象相關聯,但是由於它是一個靜態類,所以不能使用它來定義對象,而且它的所有方法都是靜態,不能使用對象來引用。下面代碼說明了使用Monitor鎖定對象的情形:
- ……
- QueueoQueue=newQueue();
- ……
- Monitor.Enter(oQueue);
- ……
- //現在oQueue對象只能被當前線程操縱了Monitor.Exit(oQueue);
- //釋放鎖
如上所示,當一個線程調用Monitor.Enter()方法鎖定對象時,這個對象就歸它所有了,其它線程想要訪問這個對象,只有等待它使用Monitor.Exit()方法釋放鎖。為了保證線程最終都能釋放鎖,你可以把Monitor.Exit()方法寫在try-catch-finally結構中的finally代碼塊裡。
對於任何一個被Monitor鎖定對象,記憶體中都儲存著與它相關的一些資訊:其一是現在持有鎖的線程的引用;其二是一個預備隊列,隊列中儲存了已經準備好擷取鎖的線程;其三是一個等待隊列,隊列中儲存著當前正在等待這個對象狀態改變的隊列的引用。
當擁有對象鎖的線程準備釋放鎖時,它使用Monitor.Pulse()方法通知等待隊列中的第一個線程,於是該線程被轉移到預備隊列中,當對象鎖被釋放時,在預備隊列中的線程可以立即獲得對象鎖。
下面是一個展示如何使用lock關鍵字和C# Monitor類來實現線程的同步和通訊的例子,也是一個典型的生產者與消費者問題。
這個常式中,生產者線程和消費者線程是交替進行的,生產者寫入一個數,消費者立即讀取並且顯示(注釋中介紹了該程式的精要所在)。
用到的系統命名空間如下:using System;using System.Threading;
首先,定義一個被操作的對象的類Cell,在這個類裡,有兩個方法:ReadFromCell()和WriteToCell.消費者線程將調用 ReadFromCell()讀取cellContents的內容並且顯示出來,生產者進程將調用WriteToCell()方法向 cellContents寫入資料。
樣本如下:
public class Cell
{
int cellContents; // Cell對象裡邊的內容
bool readerFlag = false;
// 狀態標誌,為true時可以讀取,為false則正在寫入
public int ReadFromCell( )
{
lock(this) // Lock關鍵字保證了什麼,請大家看前面對lock的介紹
{
if (!readerFlag)//如果現在不可讀取
{
try
{
//等待WriteToCell方法中調用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標誌,表示消費行為已經完成
Monitor.Pulse(this);
//通知WriteToCell()方法(該方法在另外一個線程中執行,等待中)
}
return cellContents;
}
public void WriteToCell(int n)
{
lock(this)
{
if (readerFlag)
{
try
{
Monitor.Wait(this);
}
catch (SynchronizationLockException e)
{
//當同步方法(指Monitor類除Enter之外的方法)在非同步的代碼區被調用
Console.WriteLine(e);
}
catch (ThreadInterruptedException e)
{
//當線程在等待狀態的時候中止
Console.WriteLine(e);
}
}
cellContents = n;
Console.WriteLine("Produce: {0}",cellContents);
readerFlag = true;
Monitor.Pulse(this);
//通知另外一個線程中正在等待的ReadFromCell()方法
}
}
}