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

C# 程式設計總結(三):執行緒同步機制全景解析

     編輯:關於C#

前言

多執行緒共享資源時必須協調存取。.NET 提供豐富的同步原語,本文系統介紹十大類同步機制,涵蓋原子操作、鎖、監視器、事件、互斥體、讀寫鎖、信號量、障礙、自旋鎖與自旋等待,並以「共享印表機」、「做飯」、「購票」等經典案例說明適用場景。

同步機制分類對照

類別.NET 類別/關鍵字底層模型
臨界區lock, Monitor, SpinLock互斥鎖
互斥量Mutex核心互斥體
信號量Semaphore, SemaphoreSlim計數信號量
事件AutoResetEvent, ManualResetEvent, Barrier事件通知
原子操作InterlockedCPU 原子指令
讀寫鎖ReaderWriterLockSlim共享/獨佔鎖

詳細機制與案例

1. Interlocked —— 整數原子操作

適用於計數器、旗標等簡單整數變數。`Increment`、`Decrement`、`Exchange`、`CompareExchange` 皆為原子性。

// 共享印表機旗標
static int UsingPrinter = 0;
if (0 == Interlocked.Exchange(ref UsingPrinter, 1)) {
    // 取得印表機使用權
    // ...
    Interlocked.Exchange(ref UsingPrinter, 0); // 釋放
}

2. lock / Monitor —— 最常用的臨界區

`lock(obj)` 編譯為 `Monitor.Enter/Exit` 並自動包裝 `try/finally`。鎖物件應為私有唯讀參考型別,避免 `lock(this)`、`lock(typeof(T))`、`lock("string")` 等反模式。

private static readonly object Locker = new object();
lock (Locker) {
    // 臨界區程式碼
}

3. 同步事件:AutoResetEvent / ManualResetEvent

「做飯」案例:一口鍋(共享資源),熬粥與做魚兩執行緒協調。`AutoResetEvent` 釋放單一等待執行緒後自動重置;`ManualResetEvent` 需手動 `Reset`。

AutoResetEvent potFree = new AutoResetEvent(false); // 初始無鍋可用

// 熬粥執行緒
void Porridge() {
    // 選材、淘米...
    Thread.Sleep(2000); // 熬製
    Console.WriteLine("粥好,鍋空了");
    potFree.Set(); // 通知鍋可用
}

// 做魚執行緒
void MakeFish() {
    // 洗魚、醃製...
    potFree.WaitOne(); // 等待鍋空
    // 烹調...
    potFree.Set(); // 歸還鍋
}

4. Mutex —— 跨進程同步

用法類似 `Monitor`,但可命名供跨進程使用。進程內同步首選 `Monitor`,效能較好。

static Mutex mutex = new Mutex(); // 本地 Mutex
mutex.WaitOne();
try { /* 臨界區 */ } finally { mutex.ReleaseMutex(); }

5. ReaderWriterLockSlim —— 讀多寫少最佳化

允許多執行緒並發讀取,寫入時獨佔。適合快取、配置資料等場景。

ReaderWriterLockSlim rwLock = new ReaderWriterLockSlim();
Dictionary cache = new Dictionary();

public string Read(int key) {
    rwLock.EnterReadLock();
    try { return cache[key]; } finally { rwLock.ExitReadLock(); }
}
public void Add(int key, string value) {
    rwLock.EnterWriteLock();
    try { cache.Add(key, value); } finally { rwLock.ExitWriteLock(); }
}

6. Semaphore / SemaphoreSlim —— 資源池限流

「購票」案例:3 個售票窗口,5 位顧客排隊。`Semaphore(0,3)` 初始 0 可用,主執行緒 `Release(2)` 開放 2 個窗口。

static Semaphore windows = new Semaphore(0, 3);
public void Pay(object id) {
    windows.WaitOne(); // 佔用窗口
    try { /* 結帳 */ } finally { windows.Release(); } // 釋放窗口
}

`SemaphoreSlim` 輕量、支援 `CancellationToken`、不支援跨進程,適合進程內高頻短等待。

7. Barrier (.NET 4.0+) —— 多階段並行同步點

所有參與者到達屏障後才能進入下一階段,可重複使用。

8. SpinLock / SpinWait (.NET 4.0+) —— 低延遲忙等待

適用於極短臨界區(幾十指令)、多核低爭用。`SpinLock` 為值型別,需 `ref` 傳遞;`SpinWait` 封裝兩階段等待(先忙等待再核心等待)。

// SpinLock 範例
SpinLock sl = new SpinLock();
StringBuilder sb = new StringBuilder();
Action act = () => {
    for (int i=0; i<100; i++) {
        bool got = false;
        try { sl.Enter(ref got); sb.Append(i); }
        finally { if (got) sl.Exit(); }
    }
};
Parallel.Invoke(act, act, act);

總結

無萬用同步機制,需依據資源類型、爭用強度、跨進程需求、延遲敏感度選擇。原則:優先用高層抽象(`lock`、`Interlocked`、`Concurrent` 集合、`async/await`),僅在效能分析證實瓶頸時才降級至 `SpinLock` 等低階原語。

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