.NET Framework 提供了一系列同步基元來控制線程交互并避免爭用條件。 這可大致分為三個類別:鎖定、通知和聯鎖操作。
上述類別的定義并非是絕對的:有些同步機制具有多個類別的特征;一次釋放一個線程的事件從功能上來說類似于鎖;任何鎖的釋放都可看作一個信號;而聯鎖操作可用于構造鎖。 但是,這些類別仍然是有用的。
記住線程同步是協作這一點非常重要。 只要有一個線程避開同步機制直接訪問受保護的資源,該同步機制就不是有效的。
本概述包含以下幾節:
鎖向一個線程一次提供一個資源的控制功能,或者向指定數目的線程提供此功能。 請求正在使用中的獨占鎖的線程會被阻止,直到該鎖變為可用為止。
獨占鎖
鎖定的最簡單的形式是 C# 的 lock 語句(在 Visual Basic 中為 SyncLock ),該語句可控制對代碼塊的訪問。 這種塊通常稱為臨界區。 lock 語句使通過使用 Monitor 類的 Enter 和 Exit 方法實現的,它使用 try…catch…finally 確保該鎖被釋放。
通常情況下,使用 lock 語句保護小代碼塊并且不跨越多個方法是使用 Monitor 類的最佳方法。 Monitor 類功能強大,但是容易形成孤立鎖和死鎖。
Monitor 類
Monitor 類提供了附加功能,可結合 lock 語句使用:
-
TryEnter 方法允許當前被阻止,正在等待資源的線程在指定時間間隔之后放棄。 它返回一個指示成功或失敗的布爾值,可用于檢測和避免潛在的死鎖。
-
Wait 方法由臨界區中的線程調用。 它放棄對資源的控制并阻止,直到該資源重新可用為止。
-
Pulse 和 PulseAll 方法允許要釋放鎖或調用 Wait 的線程將一個或多個線程放入就緒隊列,以使它們能夠獲取鎖。
Wait 方法重載的超時允許等待線程進入就緒隊列。
如果用于鎖的對象派生自 MarshalByRefObject ,則 Monitor 類可在多個應用程序域中提供鎖定。
Monitor 具有線程關聯。 也就是說,進入監視器的線程必須通過調用 Exit 或 Wait 才能退出。
Monitor 類不可實例化。 其方法是靜態(在 Visual Basic 中為 Shared )方法,用于可實例化的鎖對象。
有關概念性概述,請參見 監視器 。
Mutex 類
線程通過調用其 WaitOne 方法的重載請求 Mutex 。 提供了具有超時的重載,以便允許線程放棄等待。 與 Monitor 類不同,mutex 可以是局部的,也可以是全局的。 全局 mutex(也稱為命名的 mutex)在整個操作系統中可見,可用于在多個應用程序域或進程中同步線程。 局部 mutex 派生自 MarshalByRefObject ,可以跨應用程序域邊界使用。
此外, Mutex 派生自 WaitHandle ,這意味著它可用于 WaitHandle 提供的通知機制,如 WaitAll 、 WaitAny 和 SignalAndWait 方法。
與 Monitor 一樣, Mutex 具有線程關聯。 與 Monitor 不同, Mutex 是可實例化的對象。
有關概念性概述,請參見 Mutex 。
SpinLock 類
其他鎖
鎖不必是獨占的。 允許有限數目的線程并發訪問某個資源通常十分有用。 信號量和讀寫器鎖旨在控制此類池資源訪問。
ReaderWriterLock 類
ReaderWriterLockSlim 類用于更改數據的線程(編寫器)必須獨占訪問某個資源的情形。 如果編寫器未處于活動狀態,則任何數量的讀取器均可以訪問該資源(例如,通過調用 EnterReadLock 方法)。 當某個線程請求獨占訪問時(例如,通過調用 EnterWriteLock 方法),后續讀取器請求將被阻止,直至所有現有的讀取器都已退出該鎖,并且編寫器也已進入并退出該鎖。
ReaderWriterLockSlim 具有線程關聯。
有關概念性概述,請參見 讀取器/編寫器鎖 。
Semaphore 類
Semaphore 類允許指定數目的線程訪問某個資源。 請求該資源的其他線程會一直阻止,直到某個線程釋放信號量為止。
與 Mutex 類一樣, Semaphore 派生自 WaitHandle 。 Semaphore 也與 Mutex 一樣,可以是局部的,也可以是全局的。 它可以跨應用程序域邊界使用。
與 Monitor 、 Mutex 和 ReaderWriterLock 不一樣, Semaphore 不具有線程關聯。 這意味著它可以用于一個線程獲取信號量而另一個線程釋放該信號量的情形。
有關概念性概述,請參見 Semaphore 和 SemaphoreSlim 。
System.Threading :: SemaphoreSlim 是一個用于在單一進程邊界內進行同步的輕量信號量。
等待來自另一個線程的信號的最簡單的方法是調用 Join 方法,該方法將進行阻塞,直至其他線程完成。 Join 具有兩個允許阻塞的線程在經過指定時間間隔后停止等待的重載。
等待句柄提供了更為豐富的等待和通知功能。
等待句柄
等待句柄派生自 WaitHandle 類,后者又派生自 MarshalByRefObject 。 因此,等??句柄可用于跨應用程序域邊界同步線程的活動。
通過調用實例方法 WaitOne 或者靜態方法 WaitAll 、 WaitAny 或 SignalAndWait 中的一個方法,線程可由等待句柄阻止。 它們的釋放方式取決于調用的方法以及等待句柄的種類。
有關概念性概述,請參見 等待句柄 。
事件等待句柄
事件等待句柄包括 EventWaitHandle 類及其派生類 AutoResetEvent 和 ManualResetEvent 。 當通過調用 Set 方法或使用 SignalAndWait 方法通知事件等待句柄時,線程會從事件等待句柄釋放。
事件等待句柄要么自動重置自身(類似于每次得到通知時只允許一個線程通過的旋轉門),要么必須手動重置(類似于在通知前一直關閉,有人將其關閉前則一直打開的大門)。 顧名思義, AutoResetEvent 和 ManualResetEvent 分別表示前者和后者。 System.Threading :: ManualResetEventSlim 是一個用于在單一進程邊界內進行同步的輕量事件。
EventWaitHandle 可表示這兩種類型的事件,并且既可以是局部的也可以是全局的。 派生類 AutoResetEvent 和 ManualResetEvent 始終是局部的。
事件等待句柄不具有線程關聯。 任何線程都可以通知事件等待句柄。
有關概念性概述,請參見 EventWaitHandle、AutoResetEvent、CountdownEvent 和 ManualResetEvent 。
Mutex 和 Semaphore 類
因為 Mutex 和 Semaphore 類派生自 WaitHandle ,所以它們可用于 WaitHandle 的靜態方法。 例如,線程可以使用 WaitAll 方法等待,直到滿足以下三個條件為止: EventWaitHandle 接收到通知, Mutex 已釋放, Semaphore 已釋放。 類似地,線程可以使用 WaitAny 方法等待,直到滿足上述所有條件為止。
對于 Mutex 或 Semaphore ,接收到通知即意味著被釋放。 如果上述兩個類型之一用作 SignalAndWait 方法的第一個參數,該類型即被釋放。 對于具有線程關聯的 Mutex ,如果進行調用的線程不具有該 mutex,則會引發異常。 如前所述,信號量不具有線程關聯。
關卡
利用 Barrier 類,可以對多個線程進行循環同步,以便它們都在同一個點上阻塞并等待所有其他線程完成。 對于一個或多個線程在繼續某個算法的下一階段之前需要另一個線程的結果的情況,關卡很有用。 有關更多信息,請參見 屏障 (.NET Framework) 。
從 .NET Framework 4 開始,可以使用同步基元,通過盡可能避免依賴高開銷的 Win32 內核對象(例如等待句柄)來提高性能。 通常,當等待時間較短并且只有在嘗試了原始同步類型并發現它們并不令人滿意時,才應使用這些類型。 在需要跨進程通信的方案中不能使用輕量類型。
-
System.Threading :: SemaphoreSlim 是 System.Threading :: Semaphore 的輕量版本。
-
System.Threading :: ManualResetEventSlim 是 System.Threading :: ManualResetEvent 的輕量版本。
-
System.Threading :: CountdownEvent 表示一個事件,當它的計數為零時,該事件將發出信號。
-
System.Threading :: Barrier 使多個線程能夠在彼此之間進行同步,而不需要由主線程進行控制。 在所有線程已到達指定點之前,關卡會防止每個線程繼續。
從 .NET Framework 4 開始,當線程必須等待發生某個事件發出信號時或需要滿足某個條件時,可以使用 System.Threading :: SpinWait 結構,但前提是實際等待時間預計會少于通過使用等待句柄或通過其他方式阻塞當前線程所需要的等待時間。 通過使用 SpinWait ,可以指定在一個較短的時段內邊等待邊旋轉,然后只有在相應的條件在指定時間內無法得到滿足的情況下放棄旋轉(例如,通過等待或休眠)。
聯鎖操作是由 Interlocked 類的靜態方法對某個內存位置執行的簡單原子操作。 這些原子操作包括添加、遞增和遞減、交換、依賴于比較的條件交換,以及 32 位平臺上的 64 位值的讀取操作。
原子性的保證僅限于單個操作;如果必須將多個操作作為一個單元執行,則必須使用更粗粒度的同步機制。 |
盡管這些操作中沒有一個是鎖或信號,但它們可用于構造鎖和信號。 因為它們是 Windows 操作系統固有的,因此聯鎖操作的執行速度非???。
聯鎖操作可與易失存儲器保證一起使用,以編寫展示強大的非阻塞并發功能的應用程序。 但是,它們需要復雜的低級別編程,因此大多數情況下,簡單的鎖定是更好的選擇。
有關概念性概述,請參見 互鎖操作 。
更多文章、技術交流、商務合作、聯系博主
微信掃碼或搜索:z360901061

微信掃一掃加我為好友
QQ號聯系: 360901061
您的支持是博主寫作最大的動力,如果您喜歡我的文章,感覺我的文章對您有幫助,請用微信掃描下面二維碼支持博主2元、5元、10元、20元等您想捐的金額吧,狠狠點擊下面給點支持吧,站長非常感激您!手機微信長按不能支付解決辦法:請將微信支付二維碼保存到相冊,切換到微信,然后點擊微信右上角掃一掃功能,選擇支付二維碼完成支付。
【本文對您有幫助就好】元
