2011-08-04 18 views
3

雙重檢查鎖使用ReaderWriterLockSlim時必要的模式?Double Check Lock模式應該與ReaderWriteLockSlim一起使用嗎?

請考慮這種情況:我有一個dictionary。可以添加東西。但事情不能從中刪除。當添加東西時,它可能是一個非常昂貴的操作(只有幾百毫秒,但相對於應用程序的其餘部分而言仍然很昂貴)如果我想添加一些東西但它不在那裏,會不會有任何獲得通過:

  1. 第一獲取讀鎖,然後檢查存在,
  2. 然後進入一個可升級讀鎖,並且再次檢查,
  3. 然後進入一個寫鎖,如果該項目是仍然不存在於字典中?

類似以下內容:

void populateIfNotPresent(object thing) 
{ 
     _lock.EnterReadLock() ; 

     bool there = _dictionary.ContainsKey(thing); 

     _lock.ExitReadLock() ; 

     // Remember, the specs say nothing can be removed from this dictionary. 
     if (!there) 
     { 
      _lock.EnterUpgradeableReadLock() ; 

      try 
      { 
       if(!_dictionary.ContainsKey(thing)) 
       { 
        _lock.EnterWriteLock() ; 
        try 
        { 
         populate(thing) ; 
        } 
        finally 
        { 
         _lock.ExitWriteLock() ; 
        } 
       } 
      } 
      finally 
      { 
       _lock.ExitUpgradeableReadLock() ; 
      } 
     } 
} 

文檔說在同一時間可以進入升級讀鎖只有一個線程,但不會進入一個讀鎖停止任何其他線程,因此看起來在雙重檢查鎖中存在價值

您認爲如何?這是否過分矯枉過正?

+2

這是類似於例如concurrentdictionary中的原子「添加如果不存在」的方法嗎?如果是這樣,那實施可能有助於看。 –

+0

謝謝,是的,但是沒有擔心從字典中刪除的東西(再加上.NET 4之前的版本)。 –

回答

4

ReaderWriterLockSlim class(與任何其他讀寫器鎖定一樣)意味着與寫入次數相比有大量的讀取次數。

你在做什麼實際上是三重檢查,它是多餘的;你也可以輸入一個不可重寫的寫入鎖。如果該項目存在,則退出該鎖定,否則,升級到寫入鎖定。

您的方法表明讀取沒有在這裏提供任何值,因爲您有很好的執行寫入的可能性。由於可升級的寫入不會阻止任何其他讀取,因此它不應該在這裏殺死你。但是,如果這只是只有你正在做的讀/寫(或大多數發生在這裏)的地方,那麼存在一個問題,你的讀寫比率不夠高,不足以保證讀/寫鎖,你應該看看其他一些同步方法。

這就是說,最終,這是關於測試性能的;如果你要優化一個實現,你需要測量它的當前性能,以便進行比較,否則,它只是premature optimization

1

所以看起來在雙重檢查鎖中存在價值。

多少值?那麼,如果你期望看到很多緩存未命中,那麼執行可升級鎖定可能是有意義的;然而,如果你是而不是期待看到很多緩存未命中,那麼你正在做不必要的鎖定。一般來說,我會盡可能使用最簡單的解決方案來完成工作。通常情況下,優化鎖通常不是您能夠獲得最大利潤的地方,而是尋找更大的優勢來首先優化。

建議:
東西可能會給您爲您的降壓多了很多爆炸是條紋詞典(Java's StripedMap是一個不錯的開始的地方,它不應該是很難理解)。

StripedMap/StripedDictionary背後的基本想法是,你有鎖定陣列:

object[] syncs = new object[n](); 
// also create n new objects 

如果要在具有足夠大的數條條紋的地圖,讓你不得不線程數輸入方法而不發生碰撞。我沒有任何數據來支持這一點,但假設你期望多達8個線程進入地圖,那麼你可以使用8個或更多的鎖(條紋),以確保所有8個線程都可以進入同時映射。如果你想更好的「保險」「衝突」,然後創造更多的條紋,說32或64

當你進入populateIfNotPresent方法,你鎖定取決於散列碼的鎖之一:

void populateIfNotPresent(object thing) 
{ 
    lock(syncs[thing.GetHashCode()%syncs.Length]) 
    { 
     if(!dictionary.ContainsKey(thing)) 
     { 
      populate(thing); 
     } 
    } 
} 

假設您有8個條紋,現在您最多允許8個線程安全地進入並執行昂貴的操作,否則會阻塞其他7個線程。當然,這個假設是哈希函數足夠強大,以提供低重複概率的哈希。

你已經想到populateIfNotPresent是昂貴如果該項目不存在,但如果你有一個條紋字典,那麼你可以有多個線程在字典的不同部門工作,沒有相互碰撞。這會給你一個更大的好處,然後從檢查對象是否存在中刪除幾個CPU週期,因爲昂貴的操作是當對象存在時。

相關問題