首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> C語言 >> 關於C語言

C# 多執行緒機制探索(6):Monitor 監視器基礎與共享物件同步機制深度解析

     編輯:關於C語言

一、共享物件同步問題

多執行緒存取同一物件實例時,若無同步機制,會導致資料競爭:讀取到中間狀態、寫入覆蓋、不變量破壞。lock 僅能保護程式碼區塊,Monitor 提供物件級鎖 + 條件變數,支援更精細的協作模式。

二、Monitor 物件鎖機制


Queue q = new Queue();
Monitor.Enter(q);      // 取得鎖,其他執行緒阻塞
// 關鍵區:僅當前執行緒可操作 q
Monitor.Exit(q);       // 釋放鎖

最佳實務:永遠在 try/finally 中釋放:


Monitor.Enter(obj);
try { /* critical section */ }
finally { Monitor.Exit(obj); }

三、Monitor 內部三隊列模型

隊列內容狀態轉換
擁有者目前持有鎖的執行緒參考Enter → 擁有者; Exit → 清空
就緒隊列已準備好取得鎖的執行緒Pulse → 從等待隊列移入; Exit → 從就緒隊列取一個成為擁有者
等待隊列呼叫 Wait 等待條件的執行緒Wait → 釋放鎖並進入; Pulse → 移至就緒隊列

四、生產者-消費者完整實作 (Cell 類別)


public class Cell
{
    int _cellContents;
    bool _readerFlag; // true = 已生產可讀, false = 可寫

    public int ReadFromCell()
    {
        lock (this)
        {
            if (!_readerFlag)
            {
                try { Monitor.Wait(this); }
                catch (SynchronizationLockException) { }
                catch (ThreadInterruptedException) { }
            }
            Console.WriteLine($"Consume: {_cellContents}");
            _readerFlag = false;
            Monitor.Pulse(this);
        }
        return _cellContents;
    }

    public void WriteToCell(int n)
    {
        lock (this)
        {
            if (_readerFlag)
            {
                try { Monitor.Wait(this); }
                catch (SynchronizationLockException) { }
                catch (ThreadInterruptedException) { }
            }
            _cellContents = n;
            Console.WriteLine($"Produce: {_cellContents}");
            _readerFlag = true;
            Monitor.Pulse(this);
        }
    }
}

五、生產者與消費者執行緒類別


public class CellProd
{
    readonly Cell _cell;
    readonly int _quantity;
    public CellProd(Cell cell, int qty) { _cell = cell; _quantity = qty; }
    public void ThreadRun()
    {
        for (int i = 1; i <= _quantity; i++)
            _cell.WriteToCell(i);
    }
}

public class CellCons
{
    readonly Cell _cell;
    readonly int _quantity;
    public CellCons(Cell cell, int qty) { _cell = cell; _quantity = qty; }
    public void ThreadRun()
    {
        for (int i = 1; i <= _quantity; i++)
            _cell.ReadFromCell();
    }
}

六、主程式啟動


public class MonitorSample
{
    public static void Main()
    {
        var cell = new Cell();
        var prod = new CellProd(cell, 20);
        var cons = new CellCons(cell, 20);

        var producer = new Thread(prod.ThreadRun);
        var consumer = new Thread(cons.ThreadRun);

        producer.Start();
        consumer.Start();
        producer.Join();
        consumer.Join();
        Console.ReadLine();
    }
}

七、輸出結果


Produce: 1
Consume: 1
Produce: 2
Consume: 2
...
Produce: 20
Consume: 20

八、關鍵同步邏輯總結

  1. 雙緩衝旗標:_readerFlag 區分「可讀」與「可寫」狀態
  2. 條件等待:Monitor.Wait 原子性釋放鎖並進入等待隊列
  3. 單點喚醒:Monitor.Pulse 僅喚醒一個對方(因只有一個生產者/消費者)
  4. 鎖保護旗標:旗標讀寫與 Wait/Pulse 在同一 lock 內,避免競態

九、Monitor 進階特性

  • 靜態類別:不可實例化,所有方法靜態
  • 關聯物件:鎖資訊儲存在物件標頭 (SyncBlockIndex)
  • 重進入:同一執行緒可多次 Enter,需對應 Exit
  • 例外安全:SynchronizationLockException 表示在非同步區呼叫 Wait/Pulse

總結

Monitor 是 .NET 執行緒同步的基石,lock 編譯為 Monitor.Enter/Exit。掌握 Wait/Pulse 條件變數模式,可解決生產者-消費者、讀寫鎖、資源池等經典協作問題。現代高階抽象:BlockingCollection<T>、SemaphoreSlim、Channels 內部皆建立於 Monitor 之上。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved