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

.NET 多線程編程兩大核心守則:線程數控制與數據完整性保衛戰

     編輯:關於.NET

多線程威力巨大,但失控的線程數與數據競爭能瞬間摧毀系統。本文聚焦兩大核心:信號量控制併發線程上限 (防上下文切換風暴) 與 鎖/監視器防競爭條件與死鎖 (守護數據一致性),給出經典代碼模式與反模式對比。

守則一:嚴控同時運行線程數 ≤ CPU 核心數

原理:線程非越多越好。單核跑多個 CPU 密集型線程,上下文切換開銷導致總耗時 2.1~3.5 倍 於單線程。線程池雖限制創建速率,但無法限制「同時運行」數量。

最佳實踐:Semaphore 守門員模式


static Semaphore _gate = new Semaphore(0, Environment.ProcessorCount); // 計數器 = 核心數

static void Execute(object state)
{
    _gate.WaitOne(); // 獲取許可,滿則阻塞
    try { /* 核心業務邏輯 */ }
    finally { _gate.Release(); } // 歸還許可
}

static void RunBatch()
{
    for (int i = 0; i < 2000; i++)
        ThreadPool.QueueUserWorkItem(Execute); // 大量入隊,Semaphore 自動限流
}
  • Semaphore(0, maxCount):初始 0 可用,最大 maxCount (核心數)。
  • 線程池排隊任務自動等待信號量,實現「生產無限、消費受控」。
  • 替代方案 (共享狀態列表+主線程掃描) 代碼複雜、調試地獄,**強烈不推薦**。

守則二:以「臨界區」捍衛數據完整性,警惕死鎖陷阱

競爭條件經典演示


int x = 5;
// 線程 A, B 並發執行 x = x + 10;
// 可能結果: 15 (丟失更新) 或 25 (正確), 取決於調度順序。

解決方案:C# lock / VB SyncLock (本質 Monitor.Enter/Exit)


object _lockObj = new object();
int x = 5;

lock (_lockObj) // 等同 Monitor.Enter/Exit
{
    x = x + 10; // 原子操作
}
  • 同一鎖對象同一時刻僅允許一線程進入臨界區。
  • 鎖對象必須為**引用類型實例**,通常用 private readonly object 字段。

Monitor 進階:細粒度控制

當鎖持有時間需根據運行時條件動態決定 (如檢查標誌位決定是否繼續持有),直接用 Monitor.Enter/Exit/TryEnter 比 lock 更靈活。

死鎖經典場景與預防


// 線程 A
lock (obj1) { lock (obj2) { ... } }

// 線程 B
lock (obj2) { lock (obj1) { ... } }
// 交叉持有 → 死鎖

預防鐵律:

  1. 全局統一加鎖順序:所有代碼路徑按固定順序獲取鎖 (如按對象 HashCode/ID 排序)。
  2. 避免嵌套鎖:嵌套鎖是設計味道差的信號,重構縮小臨界區或合併鎖對象。
  3. 超時嘗試:Monitor.TryEnter(lockObj, timeout) 失敗則回退重試,打破循環等待。
  4. 新手陷阱:過度精細加鎖 (每個字段一把鎖) 必導致死鎖,粗粒度鎖 (一個對象一把鎖) 往往更安全、性能損耗可接受。

多線程核心心法:線程池負責「排隊」,Semaphore 負責「限流」,lock/Monitor 負責「互斥」,統一順序負責「防死鎖」。拒絕手動管理線程生命週期,擁抱結構化併發控制。

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