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

C# 多執行緒機制探索(5):lock 關鍵字與生產者消費者問題同步基礎

     編輯:關於C語言

一、生產者-消費者問題本質

共享資料結構(佇列),生產者 Enqueue,消費者 Dequeue。核心挑戰:

  • 互斥存取:同一時間只能一個執行緒修改佇列
  • 條件同步:佇列空時消費者等待,滿時生產者等待 (有界佇列)

二、lock 關鍵字:互斥區塊語法糖


lock (expression) statement_block
// 編譯為
Monitor.Enter(expression);
try { statement_block; }
finally { Monitor.Exit(expression); }

expression 選擇指南

三、經典錯誤:檢查-執行競態


// 錯誤版本
if (balance >= amount)        // 檢查
{
    Thread.Sleep(5);          // 模擬延遲
    balance = balance - amount; // 執行:此時 balance 可能已被其他執行緒修改
}

// 正確版本:lock 保護整個檢查-執行序列
lock (this)
{
    if (balance >= amount)
    {
        Thread.Sleep(5);
        balance -= amount;
        return amount;
    }
    return 0;
}

四、完整帳戶轉帳範例


using System;
using System.Threading;

internal class Account
{
    int _balance;
    Random _r = new Random();

    internal Account(int initial) { _balance = initial; }

    internal int Withdraw(int amount)
    {
        if (_balance < 0) throw new Exception("Negative Balance");

        lock (this)
        {
            Console.WriteLine($"Current Thread: {Thread.CurrentThread.Name}");
            if (_balance >= amount)
            {
                Thread.Sleep(5);
                _balance -= amount;
                return amount;
            }
            return 0; // 拒絕交易
        }
    }

    internal void DoTransactions()
    {
        for (int i = 0; i < 100; i++)
            Withdraw(_r.Next(-50, 100));
    }
}

internal class Test
{
    static Thread[] _threads = new Thread[10];

    public static void Main()
    {
        var acc = new Account(0);
        for (int i = 0; i < 10; i++)
        {
            var t = new Thread(new ThreadStart(acc.DoTransactions));
            _threads[i] = t;
            t.Name = i.ToString();
        }
        for (int i = 0; i < 10; i++) _threads[i].Start();
        Console.ReadLine();
    }
}

五、lock 進階特性

  • 重進入:同一執行緒可多次 lock 同一物件
  • 不可中斷:lock 等待不可被 Thread.Interrupt 中斷
  • 無逾時:會無限等待,死結風險較高
  • 效能:非爭用極快 (CAS 自旋),爭用進入核心等待

六、lock 適用與不適用場景

保護目標expression範例
實例成員thislock(this)
靜態成員型別本身lock(typeof(MyClass))
共享物件該物件實例lock(_sharedQueue)
專用鎖物件private readonly objectlock(_syncRoot) (推薦)
適用不適用
簡單互斥保護需條件等待 (用 Monitor.Wait/Pulse)
短小關鍵區長時間阻塞操作 (I/O、Sleep)
實例/靜態成員保護跨進程同步 (用 Mutex)
非同步等待需非同步等待 (用 SemaphoreSlim.WaitAsync)

總結

lock 是 C# 最常用的同步原語,將 Monitor.Enter/Exit 包裝為例外安全的語法糖。核心原則:保護檢查-執行原子性、選擇正確鎖物件、關鍵區越小越好。生產者-消費者需條件同步時,需進階至 Monitor.Wait/Pulse、BlockingCollection、Channels 等高階抽象。

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