淺談C# 中的lock 方法與Monitor 類的關係_以及同步與互斥

來源:互聯網
上載者:User

 

這是一箇舊瓶新裝的話題。只是將我今日的所見所思予以實踐和整理,以備往後所用。同時也提供給大家,希望能有所協助。

 

A  從單例模式說起

代碼如下:

class Program
{
 static void Main(string[] args)
 {
 Singleton demo1 = Singleton.Init();
 Singleton demo2 = Singleton.Init();
 }
}
 
public class Singleton
{
 private Singleton() { }
 
 private static Singleton instance = null;
 private static readonly object singleObj = new object();
 
 public static Singleton Init()
 {
 if (null == instance)
 {
 lock (singleObj) //singleObj 不能使用instance 代替
 {
 if (null == instance)
 {
 instance = new Singleton();
 }
 }
 }
 return instance;
 }
}

關於單例模式,大家可以參考:http://terrylee.cnblogs.com/archive/2005/12/09/293509.html

 

B  關於lock 方法

在以上單列模式的代碼中:

如果將: private static readonly object singleObj = new object();
修改為: private static readonly object singleObj = null;

將在lock( ) 方法處,拋出未處理的異常:System.ArgumentNullException: 值不可為空!

●  lock 關鍵字將語句塊標記為臨界區

●  lock( obj ) 方法中的obj 對象為:擷取獨佔鎖定的指定對象

                                     obj 對象為null 將導致 ArgumentNullException 異常。

●  lock 方法在MSIL 中會被編譯成 Monitor.Enter( ) 和 Monitor.Exit( ) 。例如:

public static void MyLock()
{
 lock (typeof(Program))
 {
 }
}

以上代碼通過lock 語句使MyLock 同步,這個方法被編譯成MSIL 後,代碼如所示:

從被標註的地區可以看到:一條lock 語句被編譯成了調用Monitor 的Enter 和Exit 的方法。
lock 的功能就相當於直接調用Monitor 的Entry 方法,並在結束後會自動調用Monitor 的Exit 方法解除鎖定。

 

C  關於Monitor 類

●  Monitor 類屬於System.Threading 命名空間;

●  Monitor 類提供同步對對象的訪問的機制;

●  使用 Monitor 鎖定對象(即參考型別)而不是實值型別;

●  Monitor 類型對於多線程操作是安全的;

 

Monitor 類的樣本一:

class Program2
{
 static void Main(string[] args)
 {
 MyMonitor1 mon_1 = new MyMonitor1();
 mon_1.Test();
 }
}
 
class MyMonitor1
{
 private object obj = new object();
 
 public void Test() 
 {
 //開始鎖定
 System.Threading.Monitor.Enter(obj);
 try
 {
 //lock 的地區
 }
 catch (Exception e)
 {
 // 異常處理代碼
 }
 finally
 {
 //解除鎖定
 System.Threading.Monitor.Exit(obj);
 }
 }
}

 

Monitor 類的樣本二:

class Program3
{
 static void Main(string[] args)
 {
 //多個線程調用Test方法
 Thread t1 = new Thread(MyMonitor2.Test);
 Thread t2 = new Thread(MyMonitor2.Test);
 t1.Start();
 t2.Start();
 }
}
class MyMonitor2
{
 private static object obj = new object();
 
 public static void Test()
 {
 //使用TryEntry方法設定一個鎖定逾時
 if (Monitor.TryEnter(obj, 2000))
 {
 try
 {
 Console.WriteLine("等待4秒開始");
 Thread.Sleep(4000);
 Console.WriteLine("等待4秒結束");
 }
 finally
 {
 //解除鎖定
 Monitor.Exit(obj);
 }
 }
 else 
 {
 Console.WriteLine("已逾時2秒!");
 }
 
 }
}

 

D  關於同步與互斥

關於同步的問題,可以使用Monitor 類來解決。

在使用Monitor 類的時候,建議將Monitor.Enter( ) 方法替換成Monitor.TryEnter( ) 方法。

 

使用Monitor.Enter( ) 方法時,代碼如下:

Monitor.Entry(lockObj);
try
{
 // lockObj的同步區
}
catch(Exception e)
{
 // 異常處理代碼
}
finally
{
 Monitor.Exit(lockObj); // 解除鎖定
}

注意:如果直接在C#來源程式中使用Monitor類,就必須調用Exit方法來顯式地解除鎖定。

 

使用Monitor.TryEnter( ) 方法時,代碼如下:

if(Monitor.TryEntry(lockObj, 1000))
{
 try
 {
 }
 finally
 {
 Monitor.Exit(lockObj);
 }
}
else
{
 // 逾時後的處理代碼
}

注意:使用TryEntry方法設定一個鎖定逾時,單位是毫秒。

上面的代碼設定了鎖定逾時時間為1秒。

如果在1秒鐘之內,lockObj 還未被解鎖,TryEntry 方法就會返回 false;

如果在1秒鐘之內,lockObj 被解鎖,TryEntry 方法就會返回 true。

這樣,可以使用TryEntry 方法來避免死結。

 

同步與互斥    樣本一:

class Program4
{
 static void Main(string[] args)
 {
 Thread A = new Thread(TestClass1.GetA);
 A.Name = "Thread_A ";
 Thread B = new Thread(TestClass1.GetB);
 B.Name = "Thread_B ";
 A.Start();
 B.Start();
 }
}
 
class TestClass1
{
 private static object resource_A = new object();
 private static object resource_B = new object();
 
 public static void GetA()
 {
 MyWrite("in GetA()");
 if (Monitor.TryEnter(resource_A, 2000))
 {
 MyWrite("get resource_A");
 GetB();
 Thread.Sleep(2000);
 Monitor.Exit(resource_A);
 MyWrite("exit resource_A");
 }
 else
 {
 MyWrite("no has resource_A");
 }
 }
 
 public static void GetB()
 {
 MyWrite("in GetB()");
 if (Monitor.TryEnter(resource_B, 1000))
 {
 MyWrite("get resource_B");
 GetA();
 Thread.Sleep(1000);
 Monitor.Exit(resource_B);
 MyWrite("exit resource_B");
 }
 else
 {
 MyWrite("no has resource_B");
 }
 }
 
 //自訂列印方法
 private static void MyWrite(string str)
 {
 Console.WriteLine(Thread.CurrentThread.Name + str);
 }
}

結果如下:

 

同步與互斥    樣本二:

class Program5
{
 static void Main(string[] args)
 {
 Thread A = new Thread(TestClass2.GetA);
 A.Name = "Thread_A ";
 Thread B = new Thread(TestClass2.GetB);
 B.Name = "Thread_B ";
 A.Start();
 B.Start();
 }
}
 
class TestClass2
{
 //獨佔鎖定的對象 A
 private static object resource_A = new object();
 //獨佔鎖定的對象 B
 private static object resource_B = new object();
 
 public static void GetA()
 {
 MyWrite("in GetA()");
 if (Monitor.TryEnter(resource_A, 1000))
 {
 MyWrite("get resource_A");
 Thread.Sleep(1000);
 //GetB();
 if (Monitor.TryEnter(resource_B, 2000))
 {
 Monitor.Exit(resource_B);
 }
 Monitor.Exit(resource_A);
 MyWrite("exit resource_A");
 }
 else
 {
 MyWrite("no has resource_A");
 }
 }
 
 public static void GetB()
 {
 MyWrite("in GetB()");
 if (Monitor.TryEnter(resource_B, 1000))
 {
 MyWrite("get resource_B");
 Thread.Sleep(1000);
 //GetA();
 if (Monitor.TryEnter(resource_A, 2000))
 {
 Monitor.Exit(resource_A);
 }
 Monitor.Exit(resource_B);
 MyWrite("exit resource_B");
 }
 else
 {
 MyWrite("no has resource_B");
 }
 }
 
 //自訂列印方法
 private static void MyWrite(string str)
 {
 Console.WriteLine(Thread.CurrentThread.Name + str); 
 }
}

結果如下:

 

參考文章:

淺談c#中使用lock的是與非      作者:Jeff Wong

同步技術之Monitor                  作者:銀河使者

 

樣本下載

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.