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

C# 多線程程式設計系列(四):深入理解同步與死鎖機制

     編輯:關於C語言

前言:多線程帶來的挑戰

雖然多線程能顯著提升程式執行效率,但也引入了資料競爭與死鎖等複雜問題。本文透過實例程式碼,深入剖析非同步操作導致的「髒資料」現象,並從 IL 層面揭示 n++ 非原子性的本質。

經典範例:30 條線程同時遞增計數器

以下程式建立 30 條線程,每條線程對共享變數 n 執行 10,000 次遞增操作。理論結果應為 300,000,但實際執行往往遠小於此值:

class Increment
{
    private int n = 0;
    private int max;
    public Increment(int max) { this.max = max; }
    public int Result { get { return n; } set { n = value; } }
    public void Inc()
    {
        for (int i = 0; i < max; i++) { n++; }
    }
}

class Program
{
    public static void Main()
    {
        Increment inc = new Increment(10000);
        Thread[] threads = new Thread[30];
        for (int i = 0; i < threads.Length; i++)
        {
            threads[i] = new Thread(inc.Inc);
            threads[i].Start();
        }
        for (int i = 0; i < threads.Length; i++)
        {
            threads[i].Join();
        }
        Console.WriteLine(inc.Result);
    }
}

IL 層面剖析:為什麼 n++ 不是線程安全的?

從 C# 原始碼看,n++ 只是簡單的自增語句,但編譯為 IL 後卻包含四條指令:

  • ldsfld:將 n 的初始值壓入評估堆疊
  • ldc.i4.1:將常數 1 壓入堆疊
  • add:彈出堆疊頂端兩值相加,結果壓回堆疊
  • stfld:彈出堆疊值,更新類別欄位 n

雖然每條 IL 指令本身是線程安全的,但 n++ 需四步完成。若任一步驟在 stfld 前因時間片輪換被中斷,其他線程讀取到的仍是舊值,導致更新丟失,產生「髒資料」。

核心結論

複合操作(讀取-修改-寫入)在多線程環境下必須採取同步機制(如 lock、Interlocked、Monitor),確保原子性,避免競態條件。

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