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

並發資料結構:volatile 變數正確使用指南

     編輯:關於.NET

volatile 關鍵字提供「輕量級同步」:保證可見性(讀取最新值)、防止指令重排、不快取於暫存器,但不具備原子性。專家建議慎用,僅在嚴格條件下適用。

背景:CPU 快取一致性與指令重排

  • 多核心 CPU 各自擁有 L1/L2/L3 快取,資料同步有延遲
  • C#/JIT 編譯器與 CPU 皆可能重排指令順序
  • CLR 提供 Thread.VolatileRead/Write、Thread.MemoryBarrier 強制快取一致
  • volatile 關鍵字編譯為對應的 Volatile 指令,適用型別:byte, sbyte, short, ushort, int, uint, char, float, bool、參考型別、特定基礎型別的列舉

volatile vs lock

特性lockvolatile
互斥性是否
可見性是是
原子性是(配合關鍵區)否
阻塞可能從不
效能(讀多寫少)較低優於 lock

正確使用的兩大鐵律

  1. 寫入操作不依賴目前值(無 x = x + 1、x++ 等「讀-改-寫」模式)
  2. 變數不參與任何不變式(不與其他變數有邏輯關聯,如 a + b == c)

簡單說:該變數完全獨立於程式狀態,甚至不記得自己當前值。

適用場景一:布林旗標(完成/停止訊號)

private volatile bool stopped;

public void Stop() { stopped = true; }

public void FindFiles() {
    while (!stopped) {
        // 搜尋檔案...
    }
}
  • 單向狀態轉換:false → true 後不再改回
  • 寫入不依賴舊值,無不變式關聯
  • 讀取頻繁(迴圈判斷),volatile 讀取開銷極低

適用場景二:物件參考發布(初始化完成訊號)

public class LargeDataObject {
    public volatile LargeDataType LargeData; // 參考型別也可 volatile

    public void InitInBackground() {
        // 大量初始化工作...
        LargeData = new LargeDataType(); // 發布完成參考
    }
}

public class OtherClass {
    public void DoWork() {
        while (true) {
            if (largeDataObject.LargeData != null) {
                Process(largeDataObject.LargeData); // 保證看到完整建構的物件
            }
        }
    }
}
  • 無 volatile 時,可能看到「參考非 null 但物件尚未完整建構」的狀態(指令重排)
  • 前提:LargeDataType 本身是執行緒安全或不可變(發布後不再修改)
  • 若發布後仍需修改,必須額外加鎖

不適用場景:計數器

// 錯誤:volatile 無法保證 ++ 原子性
private volatile int counter;
public void Increment() { counter++; } // 多執行緒下會遺失更新

混合模式:讀多寫少的執行緒安全計數器

public class ThreadSafeCounter {
    private volatile int value;
    public int Value => value; // 讀取:volatile,極快

    public int Increase() {
        lock (this) { // 寫入:加鎖保證原子
            return value++;
        }
    }
}
  • 讀取路徑無鎖、高效能
  • 寫入路徑加鎖、正確性
  • 適用「讀取遠多於寫入」場景
  • 缺陷:lock(this) 公開鎖物件,易造成死結(建議用私有 readonly object _sync = new object())

總結

volatile 提供脆弱的同步機制。
僅在變數真正獨立於其他狀態(不依賴當前值、無不變式關聯)時使用。
能用 lock、Interlocked、ConcurrentDictionary 等高階建構時,優先選擇它們。
除非你完全理解 記憶體模型、指令重排、快取一致性,否則遠離 volatile。
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved