C# 物件鎖——Monitor

桃子夭夭發表於2015-08-20

Monitor裡邊有一些static方法,可以用於在物件上獲取同步鎖,來進行一些程式同步控制操作

用法及注意點如下:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;

namespace myTest
{
    class Program
    {
        // 一個比較容易犯的錯誤
        // 使用   Monitor   鎖定物件(即引用型別)而不是值型別。將值型別變數傳遞給   Enter   時,
        //它被裝箱為物件。如果再次將相同的變數傳遞給   Enter,則它被裝箱為一個單獨物件,而且執行緒不會阻止。Monitor  
        //本應保護的程式碼未受保護。此外,將變數傳遞給   Exit   時,
        //也建立了另一個單獨物件。因為傳遞給   Exit   的物件和傳遞給   Enter   的物件不同,Monitor   
        //將引發   SynchronizationLockException
        //這種情況最好用 Interlocked 來完成
        private static int _num = 1;
        // 裝箱一下就可以了
        private static object num = _num;
        static void Main(string[] args)
        {

            Thread t1 = new Thread(new ThreadStart(addNum));
            t1.Name = "執行緒1";
            Thread t2 = new Thread(new ThreadStart(addNum));
            t2.Name = "執行緒2";
            t1.Start();
            t2.Start();
            Console.ReadLine();
        }
        //|- 擁有鎖的執行緒 lockObj->|- 就緒佇列(ready queue) |- 等待佇列(wait queue)
        // 就緒佇列:嘗試lock物件的執行緒
        // 等待佇列:在等待中, !!!不會主動!!!    去lock物件的執行緒  Monitor.wait 會使執行緒進入等待佇列
        // 如果只呼叫wait不呼叫pulse,可能使執行緒進入死鎖
        // 下面執行的時間線:  t1獲得鎖——t1列印——2000ms——t1Pulse(此時無執行緒在等待佇列,故無效)——2000ms——t1釋放鎖並進入等待佇列——
        //                 t2獲得鎖——t2列印——2000ms——t2Pulse(此時t1執行緒在等待佇列,t1進入就緒佇列)——2000ms——t2釋放鎖並進入等待佇列——
        //                 t1獲得鎖——t1Pulse(此時t2執行緒在等待佇列,t2進入就緒佇列)——t1列印——t1釋放鎖——t1退出——
        //                 t2獲得鎖——t2Pulse(此時無執行緒在等待佇列,故無效)——t2列印——t2釋放鎖——t2退出
        private static void addNum()
        {
            Boolean gotLock = false;
            try
            {
                // Monitor.Enter(num); //獲取排它鎖
                Monitor.Enter(num, ref gotLock);
                Console.WriteLine(Thread.CurrentThread.Name+ DateTime.Now.ToString() + "——————" + num);
                //釋放鎖並讓執行緒進入等待佇列,直到它重新獲得鎖
                Thread.Sleep(2000);
                //通知等待的執行緒進入就緒佇列,有鎖了
                Monitor.Pulse(num);
                Thread.Sleep(2000);
                Monitor.Wait(num);
                Monitor.Pulse(num);
            }
            finally
            {
                Console.WriteLine(Thread.CurrentThread.Name + DateTime.Now.ToString() + "——————" + num);
                if (gotLock)
                {
                    Monitor.Exit(num);
                }
            }
        }
    }
}

執行結果:

 

用Monitor類獲得物件鎖的try .. catch finally的過程還有一個語法糖,lock關鍵字

相關文章