摘要: 本系列意在記錄Windwos線程的相關知識點,包括線程基礎、線程調度、線程同步、TLS、線程池等。
這篇來說說靜態Interlocked類和ReadWrite鎖
.NET中的Interlocked
Interlocked的系列方法提供了對簡單類型的原子操作(不會被打斷的操作),因此這也是一種多線程共用變數,防止衝突爭用的方法。
比如下面的方法作用是以原子的方式遞增整數i:
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,這裡照搬過來了:
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的預設無參建構函式是不允許遞迴的,當然你也可以設定允許遞迴:
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