【Windows】執行緒漫談——.NET執行緒同步之Interlocked和ReadWrite鎖 阿新 • • 發佈:2018-11-02 摘要: 本系列意在記錄Windwos執行緒的相關知識點,包括執行緒基礎、執行緒排程、執行緒同步、TLS、執行緒池等。 這篇來說說靜態的Interlocked類和ReadWrite鎖 .NET中的Interlocked Interlocked的系列方法提供了對簡單型別的原子操作(不會被打斷的操作),因此這也是一種多執行緒共享變數,防止衝突爭用的方法。 比如下面的方法作用是以原子的方式遞增整數i: ? 1 2 int i = 0 ; Interlocked.Increment( ref i); 除此之外還包括Add、Exchange、CompareExchange、Decrement、Read和其中的某些泛型版本。如果看官使用過windows API自帶的Interlock系列函式,可能已經發現了:這裡的Interlocked類應該只是封裝了windows API的呼叫。在【Windows】執行緒漫談——執行緒同步之原子訪問中詳細闡述了Interlocked系列函式的存在意義和使用方法,作為對比下面列出.NET版本和windows API版本: .NET API 說明 Interlocked.Add InterlockedExchangeAdd 對某個變數做加法 Interlocked.Increment InterLockedIncrement 遞增變數 Interlocked.Decrement InterLockedDecrement 遞減變數 Interlocked.Exchange InterlockedExchange 對變數賦值 Interlocked.CompareExchange InterlockedCompareExchange 對變數比較後賦值(引數1與引數3比較,如果相同,把引數2賦值給引數1) 此外,Windows API還提供InitializeSListHead/InterlockedPushEntrySList/InterlockedPopEntrySList/InterlockedFlushSList/QueryDepthSList來構建單鏈表棧,參見:《Windows核心程式設計》---Interlocked原子訪問系列函式 對於Interlocked.CompareExchange,之前在園子裡看到一篇關於單例的文章:著名的雙檢鎖技術。文章大意是由於物件在通過new關鍵字建立時,可能會先將引用賦值給目標變數,再呼叫構造器,因此,在單例模式中的“雙檢測技術”可能會有隱含的bug。最後作者提出替代方案使用了Interlocked.CompareExchange,這裡照搬過來了: ? 1 2 3 4 5 6 7 8 9 10 11 12 internal sealed class MySingleton { private static MySingleton s_value = null; public static MySingleton GetMySingleton() { if (s_value != null) return s_value; MySingleton temp = new MySingleton(); Interlocked.CompareExchange(ref s_value, temp, null); return s_value; } } .NET中的ReaderWriterLock 有時對於共享資源應當區分讀和寫,因為讀的時候往往是允許多執行緒同時讀的,因為這不會造成混亂;而只有在需要寫的時候才不允許其他執行緒讀或者寫。.NET的ReaderWriterLock和ReaderWriterLockSlim為我們提供了區分讀和寫的鎖。這種方式在有些情況下通常比Monitor更高效。在MSDN中推薦使用的是ReaderWriterLockSlim類,其解釋是ReaderWriterLockSlim用一種簡單的規則處理遞迴呼叫以及更好的支援鎖升級機制,而且能更好的避免死鎖的發生,最後它比ReaderWriterLock更高效。由於兩者十分相似,所以這裡就對ReaderWriterLockSlim作個簡單的討論。 首先,應當儘量避免在同一個執行緒中多次對請求一個鎖,典型的情況就是遞迴的呼叫,因為這往往容易死鎖。因此,ReaderWriterLockSlim的預設無參建構函式是不允許遞迴的,當然你也可以設定允許遞迴: ? 1 2 3 public ReaderWriterLockSlim( LockRecursionPolicy recursionPolicy ) 對於ReaderWriterLockSlim鎖,一個執行緒試圖獲取鎖的時候分三種模式: Read Mode:讀模式,表示執行緒試圖對共享資源進行讀操作,而不會寫。ReaderWriterLockSlim.EnterReadLock\ReaderWriterLockSlim.TryEnterReadLock Write Mode:寫模式,表示執行緒試圖對共享資源進行寫操作。ReaderWriterLockSlim.EnterWriteLock\ReaderWriterLockSlim.TryEnterWriteLock Upgradeable Read Mode:讀模式,但可能將來升級成寫鎖。ReaderWriterLockSlim.EnterUpgradeableReadLock\ReaderWriterLockSlim.TryEnterUpgradeableReadLock 在不考慮同一個執行緒遞迴請求鎖的情況下: 同一時刻只能有一個執行緒獲得寫鎖,在有執行緒獲得寫鎖的時候,其他執行緒將無法獲得任何型別的鎖; 同一時刻只能有一個可升級的讀鎖,在有執行緒獲得可升級讀鎖的時候,其他執行緒只能獲得讀鎖; 同一時刻讀鎖可以被多個執行緒獲得,除了上述兩種情況; 讀鎖升級 同一時刻只能有一個執行緒獲得可升級讀鎖,當獲得可升級讀鎖的執行緒試圖獲得寫鎖的時候或可以呼叫EnterWriteLock,如果此時有執行緒沒有釋放寫鎖的話,EnterWriteLock會阻塞直到所有的讀鎖釋放,同時試圖獲得讀鎖的執行緒也將阻塞(這裡不用考慮寫鎖,因為既然可以獲得可升級讀鎖,那麼必然不存在寫鎖),這有點像“關門放狗”,關上門不讓狗進來,而把已經在裡面的狗放走。:) 請參考MSDN上的例子理解ReadWriterLockSlim:http://msdn.microsoft.com/zh-cn/library/system.threading.readerwriterlockslim.aspx ReaderWriterLockSlim和Slim讀/寫鎖 在【Windows】執行緒漫談——執行緒同步之Slim讀/寫鎖中介紹了Windows API提供的讀寫鎖同步方式。下面的表格對兩種API做了比較: .NET API 說明 ReaderWriterLockSlim構造 InitializeSRWLock ReaderWriterLockSlim.EnterWriteLock AcquireSRWLOckExclusive ReaderWriterLockSlim.TryEnterWriteLock -- ReaderWriterLockSlim.ExitWriteLock ReleaseSRWLockExclusive ReaderWriterLockSlim.EnterReadLock AcquireSRWLockShared ReaderWriterLockSlim.TryEnterReadLock -- ReaderWriterLockSlim.ExitReadLock ReleaseSRWLockShared ReaderWriterLockSlim.EnterUpgradeableReadLock -- ReaderWriterLockSlim.TryEnterUpgradeableReadLock -- ReaderWriterLockSlim.ExitUpgradeableReadLock -- -- CONDITION_VARIABLE API提供了條件變數的支援 可以遞迴特性 -- .NET提供了遞迴 從上表中可以看到,.NET的版本具有以下特點: 提供對應的TryXXX方法 提供可升級寫鎖特性 提供可遞迴的特性 不提供條件變數的用法 勞動果實,轉載請註明出處:http://www.cnblogs.com/P_Chou/archive/2012/07/24/interlocked-and-slimlock-in-net-thread-sync.html