摘要: 本系列意在記錄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