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

線程衝突詳解:必要條件、線程安全資源、函數與互斥鎖

     編輯:關於.NET

針對「多線程環境下函數是否會衝突」的常見疑問,系統梳理線程衝突定義、必要條件、線程安全資源分類、線程安全函數判定準則,以及互斥鎖正確使用姿勢。

一、什麼是線程衝突

定義:兩個或多個線程同時對同一共享資源操作導致的問題。

經典計數器超額示例:

int count = 0;
void ThreadMethod() {
    while (true) {
        if (count >= 100) break; // 檢查
        DoSomething();
        count++; // 非原子操作:讀-改-寫
    }
}

多線程下 count++ 非原子,導致「檢查通過但實際已超額」的競態條件。

必要條件:多線程 + 共享資源

  • 單線程環境無衝突(但現代應用幾乎皆多線程)。
  • 無共享資源(如純局部變量)則無衝突。

結論:只能從共享資源入手解決衝突。

二、線程安全的資源

定義:不同線程同時使用不導致衝突的資源。

1. 不可變資源

  • 常量:const decimal PI = 3.14m;
  • string 實例(.NET 不可變)、Type、Assembly 等不可變類型實例。

2. 只讀字段

readonly 字段初始化後不可變,屬線程安全。

3. 變量 ≠ 實例

static string s = "abc"; 變量 s 可被修改指向新字符串,變量不安全,但字符串實例安全。

三、線程安全的函數

判定準則:函數內僅使用線程安全資源,或僅使用局部變量(棧上,線程隔離)。

安全示例

static int Add(int a, int b) => a + b; // 參數按值傳遞,局部運算
static string GetConnStr() => connectionString; // 只讀常量

不安全示例

static void Swap(ref int a, ref int b) { ... } // ref 引用外部變量
static int Add(INumber a, INumber b) => a.Number + b.Number; // 訪問堆上對象屬性

運算符陷阱

重載運算符本質是方法調用,若操作共享可變狀態則不安全。

四、互斥鎖

保護共享資源的標準手段:lock(obj) { ... }。

鎖粒度優化

// 差:整個 DoSomething 在鎖內,退化為單線程
lock (typeof(Env)) { if (count++ >= 100) break; DoSomething(); }

// 優:僅保護共享變量讀寫
lock (typeof(Env)) { if (count++ >= 100) break; }
DoSomething(); // 耗時操作移出鎖

SyncRoot 模式

集合類提供 SyncRoot 作為專用鎖對象,防止繞過封裝直接操作內部數組(如 _list)。lock(coll.SyncRoot) 而非 lock(coll)。

總結

  • 衝突源於「多線程 + 可變共享資源」。
  • 優先使用不可變類型、局部變量、線程安全集合(Concurrent*)。
  • 必須共享可變狀態時,用 lock 保護最小臨界區。
  • 鎖對象選擇:專用 private readonly object _lock = new() 或集合 SyncRoot。
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved