并發(fā)數(shù)據(jù)結(jié)構(gòu).NETFramework中提供的讀寫鎖_第1頁
并發(fā)數(shù)據(jù)結(jié)構(gòu).NETFramework中提供的讀寫鎖_第2頁
并發(fā)數(shù)據(jù)結(jié)構(gòu).NETFramework中提供的讀寫鎖_第3頁
已閱讀5頁,還剩7頁未讀, 繼續(xù)免費(fèi)閱讀

下載本文檔

版權(quán)說明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請進(jìn)行舉報或認(rèn)領(lǐng)

文檔簡介

1、.NET kz 進(jìn)展讀取操作,那么根本不需要使用鎖;反之,假設(shè)線程只對資源進(jìn)展寫入操作,則應(yīng)當(dāng)使用互斥鎖(比方使用 Monitor 類等)。還有一種狀況,就是存在多個線程對資源進(jìn)展讀取操ReaderWriterLock 類.NETFrameworkBCL 在 1.1 版本時,給我們供給了一個 ReaderWriterLock 類來面對此種情景。但是很圓滿,Microsoft 官方不推舉使用該類。JeffreyRichter 也在他的CLRviaC#一書中對它進(jìn)展了嚴(yán)峻的批判。下面是該類不受歡送的主要緣由:AcquireReaderLock 方法比 Monitor 類的 Enter方法要慢 5

2、倍左右,而等待爭奪寫鎖甚至比 Monitor 類慢 6 倍。策略。假設(shè)某個線程完成寫入操作后,同時面臨讀線程和寫線程等待處理。ReaderWriterLock存在大量的讀線程和格外少的寫線程,這樣寫線程很可能必需長時間地等待,造成寫線程饑餓,不能準(zhǔn)時更數(shù)據(jù)。更槽糕的狀況是,假設(shè)寫線程始終等待,就會造成活鎖。反之,我們讓 ReaderWriterLock 實(shí)行寫線程優(yōu)先的策略。假設(shè)存在多個寫線程,而讀線程數(shù)量稀有,也會造成讀線程饑餓。幸運(yùn)的是,現(xiàn)實(shí)實(shí)踐中,這種狀況很少消滅。一旦發(fā)生這種狀況,我們可以實(shí)行互斥鎖的方法。ReaderWriterLock 類支持鎖遞歸。這就意味著該鎖清楚的知道目前哪個

3、線程擁有它。假設(shè)擁有該鎖的線程遞歸嘗試獲得該讀寫鎖,遞歸算法允許該線程獲得該讀寫鎖, 盡管這看起來是個很好的特性,但是實(shí)現(xiàn)這個“特性”代價太高。首先,由于多個讀線 程可以同時擁有該讀寫鎖,這必需讓該鎖為每個線程保持計數(shù)。此外,還需要額外的內(nèi) 存空間和時間來更計數(shù)。這個特性對 ReaderWriterLock 類可憐的性能奉獻(xiàn)極大。其.NET 的異步編程架構(gòu))。由于這個遞歸特性,ReaderWriterLock 不支持這種編程架構(gòu)。資源泄漏。在 .NET 2.0 之前的版本中, ReaderWriterLock 類會造成內(nèi)核對象泄露。這些對象只有在進(jìn)程終止后才能再次回收。幸運(yùn)的是,.NET 2.

4、0 修正了這個 Bug 。ReaderWriterLockUpgradeToWriteLock 方法。這個方法實(shí)際上在更到寫鎖前先釋放了讀鎖。這就讓其他線程有時機(jī)在此期間乘虛而入,從而獲得讀寫鎖且轉(zhuǎn)變狀態(tài)。假設(shè)先更到寫鎖,然后釋放讀鎖。假設(shè)兩個線程同時更將會導(dǎo)致另外一個線程死鎖。MicrosoftReaderWriterLockSlim 類。原來可以在原有的 ReaderWriterLock 類上修正錯誤,但是考慮到兼容性和已存在的 API , Microsoft 放棄了這種做法。 固然也可以標(biāo)記ReaderWriterLock 類為 Obsolete,但是由于某些緣由,這個類還有存在的必要。

5、ReaderWriterLockSlim 類的 ReaderWriterLockSlim 類支持三種鎖定模式:Read,Write,UpgradeableRead。這三種模式對應(yīng)的方法分別是 EnterReadLockEnterWriteLockEnterUpgradeableReadLock 。 再就是與此對應(yīng)的 kkk ExitReadLock,ExitWriteLock,ExitUpgradeableReadLock。Read 和 Writer 鎖定模式比較簡潔易懂:Read 模式是典型的共享鎖定模式,任意數(shù)量的線程都可以在該模式下同時獲 得鎖;Writer 模式則是互斥模式,在該模式下

6、只允許一個線程進(jìn)入該鎖。UpgradeableRead鎖定模式可能對于大多數(shù)人來說比較穎,但是在數(shù)據(jù)庫領(lǐng)域卻眾所周知。這個的讀寫鎖類性能跟 Monitor 類大致相當(dāng),或許在 Monitor 類的 2 倍之內(nèi)。而且鎖優(yōu)先讓寫線程獲得鎖,由于寫操作的頻率遠(yuǎn)小于讀操作。通常這會導(dǎo)致更好的可伸縮性。起初,ReaderWriterLockSlimCTP 的代碼還PrefersReaders, PrefersWritersAndUpgrades 和 Fifo 等競爭策略。但是這些策略雖然添加起來格外簡潔,但是會導(dǎo)致狀況格外的簡單。所以 Microsoft 最終打算供給一個能夠在大多數(shù)狀況下良好工作的簡潔

7、模型。ReaderWriterLockSlim 的更鎖現(xiàn)在讓我們更加深入的爭辯一下更模型。UpgradeableRead 鎖定模式允許安全的從 Read或 Write 模式下更。還記得從前 ReaderWriterLock 的更是非原子性,危急的操作嗎致死鎖。鎖一次只允許一個線程處在 UpgradeableRead 模式下。using System;using System.Linq;using System.Threading;namespace Lucifer.CSharp.Sampleclass Programprivate ReaderWriterLockSlim rwLock = n

8、ew ReaderWriterLockSlim;void Samplebool isUpdated = true; rwLock.EnterUpgradeableReadLock;using System;using System.Linq;using System.Threading;namespace Lucifer.CSharp.Sampleclass Programprivate ReaderWriterLockSlim rwLock = new ReaderWriterLockSlim;void Samplebool isUpdated = true; rwLock.EnterUpg

9、radeableReadLock;tryif(/*/)rwLock.EnterWriteLock;try/finallyfinallyrwLock.ExitWriteLock;elserwLock.EnterReadLock; rwLock.ExitUpgradeableReadLock;isUpdated = false;try/finallyrwLock.ExitReadLock;finallyif (isUpdated) rwLock.ExitUpgradeableReadLock;ReaderWriterLockSlim 的遞歸策略和升級到寫鎖之外, 全部的遞歸懇求都不允許。這意味著你

10、不能連續(xù)兩次調(diào)用EnterReadLockCLR 將會拋出 LockRecursionException特別。固然,你可以使用 LockRecursionPolicy.SupportsRecursion 的構(gòu)造函數(shù)參數(shù)讓該讀寫鎖支持遞歸鎖定。但不建議對的開發(fā)使用遞歸,由于遞歸會帶來不必要的簡單狀況, 從而使你的代碼更簡潔消滅死鎖現(xiàn)象。時懇求寫鎖。Microsoft 曾經(jīng)考慮供給這樣的支持,但是這種狀況太簡潔導(dǎo)致死鎖。所以Microsoft 最終放棄了這個方案。如 IsReadLockHeld, IsWriteLockHeld 和 IsUpgradeableReadLockHeld 。你也可以通

11、過WaitingReadCount,WaitingWriteCount 和 WaitingUpgradeCount 等屬性來查看有多少線程正在等待持有特定模式下的鎖。CurrentReadCount 屬性則告知目前有多少并發(fā)讀線程。RecursiveReadCountRecursiveWriteCount 和 RecursiveUpgradeCount 則告知目前線程進(jìn)入特定模式鎖定狀態(tài)下的次數(shù)。小結(jié)這篇文章分析了 .NET 中供給的兩個讀寫鎖類。然而 .NET 3.5 供給的讀寫鎖ReaderWriterLockSlim 類 消 除 了 ReaderWriterLockReaderWrite

12、rLock 相比,性能有了極大提高。更具有原子性,也可以極大避開死鎖。更ReaderWriterLockSlim 來代替ReaderWriterLock 類。Update 于 2022-12-07 0:06WindowsVista 及其以后的版本增了一個 SRWLockWindows 內(nèi)核大事機(jī)制為根底而構(gòu)建。它的設(shè)計比較有意思。SRW 鎖不支持遞歸。Windows Kernel 團(tuán)隊(duì)認(rèn)為支持遞歸會造成額外系統(tǒng)開銷,緣由是為了維SRW 鎖也不支持從共享訪問升級到獨(dú)占訪問,同時也不支持從觸。我對其進(jìn)展了 .NET 封裝。代碼如下:usingusing System;using System.Th

13、reading;using System.Runtime.InteropServices;namespace Lucifer.Threading.Lock/ / Windows NT 6.0 才支持的讀寫鎖。/ / 請留意,這個類只能在 NT 6.0 及以后的版本中才能使用。public sealed class SRWLockprivate IntPtr rwLock;/ / 該鎖不支持遞歸。/ public SRWLockInitializeSRWLock(out rwLock);/ / 獲得讀鎖。/ public void EnterReadLockAcquireSRWLockShare

14、d(ref rwLock);/ / 獲得寫鎖。/ public void EnterWriteLockAcquireSRWLockExclusive(ref rwLock);/ / 釋放讀鎖。/ public void ExitReadLockReleaseSRWLockShared(ref rwLock);/ / 釋放寫鎖。/ public void ExitWriteLockReleaseSRWLockExclusive(ref rwLock);DllImport(“Kernel32“, CallingConvention = CallingConvention.Winapi, Exact

15、Spelling = true)private static extern void InitializeSRWLock(out IntPtr rwLock);DllImport(“Kernel32“, CallingConvention = CallingConvention.Winapi, ExactSpelling = true)private static extern void AcquireSRWLockExclusive(ref IntPtr rwLock);DllImport(“Kernel32“, CallingConvention = CallingConvention.W

16、inapi, ExactSpelling = true)private static extern void AcquireSRWLockShared(ref IntPtr rwLock);DllImport(“Kernel32“, CallingConvention = CallingConvention.Winapi, ExactSpelling = true)private static extern void ReleaseSRWLockExclusive(ref IntPtr rwLock); DllImport(“Kernel32“, CallingConvention = Cal

17、lingConvention.Winapi, ExactSpellingactSpelling = true)private static extern void ReleaseSRWLockShared(ref IntPtr rwLock);此外,在其他平臺也有一些有意思的讀寫鎖。比方 Linux 內(nèi)核中的讀寫鎖和 Java 中的讀寫鎖。感興趣的同學(xué)可以自己爭辯一番。using System;using System;using System.Threading;namespace Lucifer.CSharp.Samplepublic class SynchronizedCachepriv

18、ate ReaderWriterLockSlim cacheLock = new ReaderWriterLockSlim; private Dictionary innerCache = new Dictionary;public string Read(int key)cacheLock.EnterReadLock;tryreturn innerCachekey;finallycacheLock.ExitReadLock;public void Add(int key, string value)cacheLock.EnterWriteLock;tryinnerCache.Add(key,

19、 value);finallycacheLock.ExitWriteLock;public bool AddWithTimeout(int key, string value, int timeout)if (cacheLock.TryEnterWriteLock(timeout)tryinnerCache.Add(key, value);finallycacheLock.ExitWriteLock;elsereturn true;return false;public AddOrUpdateStatus AddOrUpdate(int key, string value)cacheLock.

20、EnterUpgradeableReadLock;trystring result = null;if (innerCache.TryGetValue(key, out result)if (result = value)return AddOrUpdateStatus.Unchanged;elsecacheLock.EnterWriteLock;tryinnerCachekey = value;finallycacheLock.ExitWriteLock;return AddOrUpdateStatus.Updated;elsecacheLock.EnterWriteLock;tryinne

21、rCache.Add(key, value);finallycacheLock.ExitWriteLock;return AddOrUpdateStatus.Added;finallycacheLock.ExitUpgradeableReadLock;public void Delete(int key)cacheLock.EnterWriteLock;tryinnerCache.Remove(key);finallycacheLock.ExitWriteLock;public enum AddOrUpdateStatusAdded, Updated, Unchanged;再次 Update

22、于 2022-12-07 0:47lock-free 方案。但是 lock-free 有著固有缺陷:極難編碼,極難證明其正確性。讀寫鎖方案的應(yīng)用范圍更加廣泛一些。再說 lock-free 編程lock-free 編程實(shí)在讓人又愛又恨。博主以前曾經(jīng)寫過幾篇關(guān)于 lock-free 編程的文章。比方關(guān)于無鎖編程、并發(fā)數(shù)據(jù)構(gòu)造:迷人的原子。假設(shè)想更加深入的了解和實(shí)踐 lock-free 編程,可以參考 CLR 2.0 Memory Model、并發(fā)數(shù)據(jù)構(gòu)造:Stack。這篇文章并不打算連續(xù)闡述如何使用 lock-freelock-free 有個更加全面的生疏。說到 lock-freeCASCAS 是

23、英文 CompareandSwap 的簡寫。在 Windows 和 .NETInterlockedAPIx86 架構(gòu) CPU 對應(yīng)的匯編指令有 XCHG、CMPXCHG、INC 等,固然還得加上 LOCK 作為前綴(更多信息請看 并發(fā)數(shù)據(jù)構(gòu)造:迷人的原子)。CAS 原語在輕度和中度爭用狀況下確實(shí)可以大幅度提高程序性能。但凡事有利必有弊,CAS原語極度扼殺了程序的可伸縮性(其他缺點(diǎn)請看關(guān)于無鎖編程有點(diǎn)偏激,但事實(shí)如此。請容博主細(xì)細(xì)道來:CASIntel 和 AMD 的 CPU 承受了一種叫做MOSEICAS 操作相對本錢低廉。但一旦資源爭用,就會引起緩存失效和總線占用。緩存越失效,總線越被占CA

24、SCAS 內(nèi)存操作來說也是如此,但 CAS 狀況更加槽糕。CAS 操作要比一般內(nèi)存操作花費(fèi)更多 CPU寫緩沖區(qū)與穿越內(nèi)存柵欄限制和需求以及編譯器對 CAS 操作優(yōu)化的力量。CASCAS 操作失敗將導(dǎo)致重嘗試某些指令(典型的回滾操作)。即便沒有任何爭用,它也會做一些無用功。不管成功或失敗都會增加爭用的風(fēng)險。大多數(shù) CAS 操作發(fā)生在鎖進(jìn)入和退出時。盡管鎖可由單一 CAS 操作構(gòu)建,但 .NET CLRMonitor 類卻使用了兩個(一個在 Enter 方法,另一個在 Exit 方法)。lock-free 算法也經(jīng)常使用 CAS 原語來代替使用鎖機(jī)制。但是由于內(nèi)存重組,這樣的算法也經(jīng)常需要顯式的柵

25、欄,即便使用了 CAS 指令。鎖機(jī)制格外邪惡,但大多數(shù)合格的開發(fā)人員都知道讓鎖持有盡量少的時間。因此,雖然鎖機(jī)制讓人格外厭煩,且影響性能。但相對于大量,頻繁的 CAS操作而言,它卻并不影響程序的可伸縮性。舉個很簡潔的例子,增加計數(shù) 100,000,000 次。要做到這樣,有幾種方式。假設(shè)僅運(yùn)行在單核單處理器上,我們可以使用一般的內(nèi)存操作:static volatile int counter = 0;static void BaselineCounterfor (int i = 0; i Count; i+)counter+;很明顯,上述代碼例如不是線程安全的,但給計數(shù)器供給了一個很好的時間基

26、準(zhǔn)。下面我們使用 LOCK INC 來作為線程安全的第一種方式:static volatile int counter = 0;static void LockIncCounterfor (int i = 0; i Count; i+)Interlocked.Increment(ref counter);些驗(yàn)證(比方內(nèi)存溢出保護(hù)),我們通常會使用這種方式。就是使用 CMPXCHG(即 CAS):static volatile int counter = 0;static void CASCounterfor (int i = 0; i Count; i+)int oldValue;dooldV

27、alue = counter;while(Interlocked pareExchange(refcounter,oldValue+1,oldValue)!=oldValue);現(xiàn)在問一個有意思的問題:當(dāng)緩存爭用時,哪一個方法更慢?結(jié)果可能會讓你大吃一驚哦。在 Intel 4 核處理器下測試結(jié)果如下:圖中,當(dāng) CPU 使用 2 個核時,BaselineCounter 方法是單核單路狀況的 2.11 倍。其他狀況類似。通過結(jié)果比對,我們可以得知:更多的并發(fā)性導(dǎo)致結(jié)果更加槽糕。這很大局部緣由由內(nèi)存爭用所致。當(dāng) CASCASCounter 方法的在多核處理器上的性能(具體技巧可以參考夏天是個好季節(jié)兄

28、的自己動手實(shí)現(xiàn)一個輕量級的信號量一二)。這可以大大削減活鎖和關(guān)聯(lián)內(nèi)聯(lián)阻礙鎖消耗的時間。 延遲訪問共享內(nèi)存可以極大緩解壓力。比方插入 2 個函數(shù)調(diào)用,我們得到了如下數(shù)據(jù):插入 64 個函數(shù)調(diào)用之后,數(shù)據(jù)又變成了如下所示:這個時候,我們看到多核所花費(fèi)的時間少于單核了。這就是我們使用并行所帶來的加速??吹竭@里,我們可能會想,既然從 2 到 64 個函數(shù)調(diào)用使得結(jié)果越來越好,那么超過 64 個函數(shù)調(diào)用豈不是會變得更好?實(shí)際上,在插入 128 個函數(shù)調(diào)用之后,加速已經(jīng)到達(dá)極限。結(jié)果如下所示:如何計算加速比,請參考 并行思維 II。天下沒有免費(fèi)的午餐,CAS 也不例外。我們應(yīng)當(dāng)慎之又慎的將 lock-free CAS 代碼放到我結(jié):共享是魔鬼。它從根本上限制應(yīng)用程

溫馨提示

  • 1. 本站所有資源如無特殊說明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請下載最新的WinRAR軟件解壓。
  • 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請聯(lián)系上傳者。文件的所有權(quán)益歸上傳用戶所有。
  • 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁內(nèi)容里面會有圖紙預(yù)覽,若沒有圖紙預(yù)覽就沒有圖紙。
  • 4. 未經(jīng)權(quán)益所有人同意不得將文件中的內(nèi)容挪作商業(yè)或盈利用途。
  • 5. 人人文庫網(wǎng)僅提供信息存儲空間,僅對用戶上傳內(nèi)容的表現(xiàn)方式做保護(hù)處理,對用戶上傳分享的文檔內(nèi)容本身不做任何修改或編輯,并不能對任何下載內(nèi)容負(fù)責(zé)。
  • 6. 下載文件中如有侵權(quán)或不適當(dāng)內(nèi)容,請與我們聯(lián)系,我們立即糾正。
  • 7. 本站不保證下載資源的準(zhǔn)確性、安全性和完整性, 同時也不承擔(dān)用戶因使用這些下載資源對自己和他人造成任何形式的傷害或損失。

評論

0/150

提交評論