在C#的多線程編程中, Monitor
是一種用于同步多個線程訪問共享資源的機(jī)制。 它是基于對象的鎖定機(jī)制,能夠有效地控制對代碼塊的訪問,防止數(shù)據(jù)的不一致,其實(shí)與lock基本一樣的。 本文將詳細(xì)介紹 Monitor
的特點(diǎn)、用法,并提供多個示例以展示其應(yīng)用。
`Monitor` 的特點(diǎn) 獨(dú)占性訪問 :Monitor
通過鎖定對象,確保同一時刻只有一個線程可以訪問被鎖定的代碼塊。
高效性 :相比于 Mutex
,Monitor
的性能開銷較小,適合在同一進(jìn)程中的多線程環(huán)境中使用。
支持條件變量 :Monitor
允許線程在等待某個條件時釋放鎖,這樣其他線程可以獲得鎖,避免資源的浪費(fèi)。
易于使用 :Monitor
提供了較為簡單的 APIs,如 Enter
、Exit
、Wait
、Pulse
和 PulseAll
。
使用 `Monitor` 的基本語法 以下是 Monitor
的基本用法示例:
object lockObject = new object(); Monitor.Enter(lockObject); // 請求鎖 try { // ... 訪問共享資源 } finally { Monitor.Exit(lockObject); // 釋放鎖 }
示例:使用 `Monitor` 實(shí)現(xiàn)線程安全的計數(shù)器 以下示例展示了如何使用 Monitor
來實(shí)現(xiàn)一個線程安全的計數(shù)器,確保只有一個線程可以對計數(shù)器進(jìn)行更新。
namespace AppMonitor01 { internal class Program { private static int counter = 0 ; // 共享資源 private static object lockObject = new object(); // 用于鎖定代碼塊 static void Main (string [] args) { Thread[] threads = new Thread[5 ]; for (int i = 0 ; i < threads.Length; i++) { threads[i] = new Thread(IncrementCounter); threads[i].Start(); } foreach (var thread in threads) { thread.Join(); } Console.WriteLine($"最終計數(shù)器的值: {counter}" ); } static void IncrementCounter () { for (int i = 0 ; i < 1000 ; i++) { Monitor.Enter(lockObject); // 請求鎖 try { counter++; // 增加計數(shù) } finally { Monitor.Exit(lockObject); // 確保釋放鎖 } } } } }
代碼解析 共享資源 :counter
是一個靜態(tài)變量,由多個線程共享。
鎖對象 :lockObject
是一個用于同步的對象,所有線程通過該對象進(jìn)行控制。
線程創(chuàng)建和啟動 :創(chuàng)建5個線程并啟動它們,每個線程執(zhí)行 IncrementCounter
方法。
計數(shù)器增加 :在 Monitor.Enter(lockObject)
后,只有獲得鎖的線程才能執(zhí)行 counter++
。
鎖的釋放 :在 finally
塊內(nèi)調(diào)用 Monitor.Exit(lockObject)
,確保鎖總是釋放,即使發(fā)生異常。
等待線程完成 :主線程使用 Join
方法確保所有工作線程完成后再輸出 counter
的值。
示例:使用 `Monitor` 的條件變量 使用 Monitor
的條件變量,允許線程在特定條件下等待以釋放鎖,以下示例展示了如何使用 Monitor.Wait
和 Monitor.Pulse
。
using System;using System.Threading;class Program { private static object lockObject = new object(); // 用于同步的對象 private static bool isReady = false ; // 條件變量 static void Main (string [] args) { Thread workerThread = new Thread(Worker); Thread notifierThread = new Thread(Notifier); workerThread.Start(); notifierThread.Start(); workerThread.Join(); notifierThread.Join(); } static void Worker () { Console.WriteLine("Worker 正在等待通知..." ); lock (lockObject) { while (!isReady) { Monitor.Wait(lockObject); // 等待通知 } Console.WriteLine("Worker 收到通知,開始工作。" ); } } static void Notifier () { Console.WriteLine("Notifier 正在處理..." ); Thread.Sleep(2000 ); // 模擬處理時間 lock (lockObject) { isReady = true ; Monitor.Pulse(lockObject); // 發(fā)送通知 Console.WriteLine("Notifier 已發(fā)送通知。" ); } } }
代碼解析 條件變量 :isReady
是一個布爾變量,用于控制 Worker
線程的執(zhí)行。
工作線程 :Worker
線程在獲得鎖后,檢查 isReady
的值,如果為 false
,則調(diào)用 Monitor.Wait
方法,釋放鎖并等待通知。
通知線程 :Notifier
線程在處理完畢后,通過 Monitor.Pulse
發(fā)送通知,喚醒等待的 Worker
線程。
工作執(zhí)行 :一旦 Worker
收到通知,就會繼續(xù)執(zhí)行相應(yīng)的工作。
示例:使用 `Monitor.PulseAll` 進(jìn)行喚醒所有等待線程 在某些情況下,可能需要喚醒所有等待的線程,可以使用 Monitor.PulseAll
方法。以下示例展示了如何使用 Monitor.PulseAll
。
using System;using System.Threading;class Program { private static object lockObject = new object(); private static int readyCount = 0 ; // 準(zhǔn)備好的線程計數(shù) private static int neededCount = 3 ; // 需要滿多少個線程 static void Main (string [] args) { for (int i = 0 ; i < 5 ; i++) { Thread thread = new Thread(Worker); thread.Start(i + 1 ); } Thread.Sleep(2000 ); // 等待一些時間讓所有線程開始 NotifyThreads(); // 調(diào)用通知方法 } static void Worker (object index) { Console.WriteLine($"線程 {index} 正在準(zhǔn)備..." ); lock (lockObject) { readyCount++; if (readyCount < neededCount) { Monitor.Wait(lockObject); // 等待通知 } Console.WriteLine($"線程 {index} 繼續(xù)執(zhí)行。" ); } } static void NotifyThreads () { Console.WriteLine("準(zhǔn)備好所有線程。" ); lock (lockObject) { // 使得所有等待的線程都能繼續(xù) Monitor.PulseAll(lockObject); } } }
代碼解析 準(zhǔn)備計數(shù) :readyCount
用于計算已準(zhǔn)備好的線程數(shù)量,而 neededCount
定義了需要滿的線程數(shù)量。
工作線程 :每個 Worker
在線程啟動時增加 readyCount
計數(shù),如果未達(dá)到需要數(shù)量的線程,則調(diào)用 Monitor.Wait
進(jìn)入等待狀態(tài)。
通知方法 :當(dāng) NotifyThreads
被調(diào)用時,會使用 Monitor.PulseAll
喚醒所有等待的線程,使它們能夠繼續(xù)執(zhí)行。
使用 `Monitor` 的注意事項(xiàng) 確保釋放鎖 :始終使用 try…finally
構(gòu)造以確保在異常或早期返回時仍然釋放鎖。
避免死鎖 :在使用 Monitor
時,確保不以不當(dāng)順序獲取多個鎖,避免死鎖現(xiàn)象。
高并發(fā)下的性能 :雖然 Monitor
的性能相對較好,但在高并發(fā)情況下,可能仍會造成性能瓶頸。
條件變量的使用 :使用 Monitor.Wait
和 Monitor.Pulse
時,確保它們在相同的鎖定對象上調(diào)用,以確保正確執(zhí)行。
結(jié)論 Monitor
是C#中一種強(qiáng)大的多線程同步機(jī)制,能夠有效管理線程對共享資源的訪問。通過上述示例,您可以看到如何利用 Monitor
來實(shí)現(xiàn)線程安全的操作,并控制線程的執(zhí)行順序。合理地使用 Monitor
可以顯著提高多線程應(yīng)用的可靠性和效率。在應(yīng)用中,開發(fā)者應(yīng)關(guān)注性能、錯誤處理及可能的競爭條件,以確保安全和高效性。
該文章在 2025/1/26 9:35:08 編輯過