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

C# 線程同步深度解析:Monitor 類的核心機制與實戰應用

     編輯:關於C#

前言:從 lock 到 Monitor 的本質

在 C# 多線程開發中,lock 關鍵字是最常見的同步手段。然而,許多開發者可能不知道,lock 實際上只是編譯器提供的語法糖,其底層實際調用的是 System.Threading.Monitor 類。理解 Monitor 的工作原理,不僅能幫助我們更靈活地控制線程同步,還能掌握避免死鎖、實現超時機制等進階技巧。

Monitor 與 lock 的編譯關係

觀察以下簡單的同步代碼:

public static void MyLock()
{
    lock (typeof(Program))
    {
    }
}

編譯為 IL 後,會發現 lock 被轉換為 Monitor.Enter 和 Monitor.Exit 的調用。這意味著 lock 只是 Monitor 的一層封裝,並在結束時自動調用 Exit 釋放鎖,類似於 using 語句的資源自動釋放機制。

直接使用 Monitor 的正確模式

若直接使用 Monitor 類,必須顯式調用 Exit,並配合 try-finally 確保鎖在任何情況下(異常、返回、正常執行)都能被釋放:

Monitor.Enter(lockObj);
try
{
    // 同步代碼區
}
catch (Exception e)
{
    // 異常處理
}
finally
{
    Monitor.Exit(lockObj); // 確保解鎖
}

Monitor.TryEnter:帶超時的鎖獲取

Monitor 提供了 TryEnter 方法,允許指定等待超時時間(毫秒)。這對於避免死鎖至關重要:

if (Monitor.TryEnter(lockObj, 1000)) // 等待 1 秒
{
    try
    {
        // 臨界區操作
    }
    finally
    {
        Monitor.Exit(lockObj);
    }
}
else
{
    // 超時處理邏輯
}

若超時時間設為 Timeout.Infinite,則等同於 Enter;設為 0 則立即返回不等待。

實戰:利用 TryEnter 破解死鎖

經典的死鎖場景是兩個線程按相反順序請求兩把鎖。使用 TryEnter 並設置超時,可使線程在無法獲取鎖時主動退出,從而打破死鎖條件:

class Program
{
    private static Object objA = new Object();
    private static Object objB = new Object();

    public static void LockA()
    {
        if (Monitor.TryEnter(objA, 1000))
        {
            Thread.Sleep(1000);
            if (Monitor.TryEnter(objB, 2000))
            {
                Monitor.Exit(objB);
            }
            else
            {
                Console.WriteLine("LockB timeout");
            }
            Monitor.Exit(objA);
        }
        Console.WriteLine("LockA");
    }

    public static void LockB()
    {
        if (Monitor.TryEnter(objB, 2000))
        {
            Thread.Sleep(2000);
            if (Monitor.TryEnter(objA, 1000))
            {
                Monitor.Exit(objA);
            }
            else
            {
                Console.WriteLine("LockA timeout");
            }
            Monitor.Exit(objB);
        }
        Console.WriteLine("LockB");
    }

    public static void Main()
    {
        Thread threadA = new Thread(LockA);
        Thread threadB = new Thread(LockB);
        threadA.Start();
        threadB.Start();
        Thread.Sleep(4000);
        Console.WriteLine("線程結束");
    }
}

運行結果顯示,兩個線程均能在超時後繼續執行,避免了永久阻塞。

總結

Monitor 類不僅是 lock 的底層實現,更提供了 TryEnter、Wait、Pulse 等豐富同步原語。掌握其用法,能讓我們在複雜併發場景下實現更精細、更安全的線程控制。

IL 代碼示意圖 死鎖避免運行結果

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