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

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