1. 程式人生 > >【Windows】執行緒漫談——.NET執行緒同步之Interlocked和ReadWrite鎖

【Windows】執行緒漫談——.NET執行緒同步之Interlocked和ReadWrite鎖

摘要: 本系列意在記錄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的ReaderWriterLockReaderWriterLockSlim為我們提供了區分讀和寫的鎖。這種方式在有些情況下通常比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