2010-09-29 71 views
1

以下代碼將從網站調用,因此在非靜態類中有一個靜態字典對象需要線程安全。基本上,代碼的目的是封裝邏輯並維護存儲在CounterContainer實例中的perfmon計數器的生命週期。構造函數被稱爲傳入instanceName。構造函數需要檢查該實例名稱的CounterContainer是否已被定義並存儲在字典中。如果是這樣,它可以(並且必須)使用該實例。如果不是,它會創建一個CounterContainer實例,將其存儲在字典中,然後使用該實例。要使用的CounterContainer實例存儲在一個非靜態成員中,因此線程安全。這是鎖定線程安全的正確對象嗎?

作爲使用靜態字典的唯一地方是在構造函數中,它讓我覺得在它將被訪問期間鎖定字典是安全的嗎?這是否會導致任何不可預見的問題,如阻塞/死鎖?我什麼都看不到,但過去並沒有太多需要考慮這種事情。我也考慮過lock(this):但我不認爲這樣會工作,因爲它只會鎖定被創建的PerformanceCounters的實例,而不是基礎的靜態字典(所以不會是線程安全的)。

namespace ToolKit 
{ 
    using System; 
    using System.Diagnostics; 
    using System.Collections.Generic; 

    public class PerformanceCounters : IPerformanceCounters 
    { 
     private static Dictionary<string, CounterContainer> _containers = new Dictionary<string, CounterContainer>(); 
     private CounterContainer _instanceContainer; 

     public PerformanceCounters(string instanceName) 
     { 
      if (instanceName == null) throw new ArgumentNullException("instanceName"); 
      if (string.IsNullOrWhiteSpace(instanceName)) throw new ArgumentException("instanceName"); 

      // Is this the best item to lock on? 
      lock (_containers) 
      { 

       if (_containers.ContainsKey(instanceName)) 
       { 
        _instanceContainer = _containers[instanceName]; 
        return; 
       } 

       _instanceContainer = new CounterContainer(instanceName); 
       _containers.Add(instanceName, _instanceContainer); 
      } 
     } 
     public void Start() 
     { 
      _instanceContainer.AvgSearchDuration.Start(); 
     } 

     public void FinishAndLog() 
     { 
      _instanceContainer.SearchesExecuted.Increment(); 
      _instanceContainer.SearchesPerSecond.Increment(); 
      _instanceContainer.AvgSearchDuration.Increment(); 
     } 
    } 
} 
+2

你有沒有考慮過使用ConcurrentDictionary類?那麼你不必擔心它 - 也可以重複到這個問題:http://stackoverflow.com/questions/440957/c-concurrency-locking-and-dictionary-objects – BrokenGlass 2010-09-29 13:07:40

+0

@BrokenGlass:對不起,我錯過了,是的,他們幾乎完全相同的情況。在沒有聽說過ConcurrentDictionary類之前,它也被提出來作爲對此的迴應。 – 2010-09-29 17:30:14

回答

0

在我看來,相當普遍的是有一個專門用於鎖定的顯式對象實例。

private readonly object containerLock = new object(); 
... 
lock (containerLock) { 
... 
} 

另一個技巧:如果要針對.NET 4中,可考慮使用ConcurrentDictionary

+0

-1:字典是靜態的;你的鎖被實例化。 – x0n 2010-09-29 13:10:35

+0

但是,對於引用ConcurrentDictionary的+1,因爲GetOrAdd(...)傳入一個函數來執行創建的功能看起來正是我想要做的。 @flq:謝謝你,這是值得建立在@ x0n所說的,使用對象的方式與「this」相同,因爲兩者都是基於實例的。如果你讓對象是靜態的,那麼它就像我提供的代碼一樣,但是我鎖定了我正在使用的確切對象。 – 2010-09-29 13:18:43

+0

將字靜態放入,然後 – flq 2010-09-29 13:25:36

2

是的,它會工作,因爲_containers是靜態的。

你可能想看看ReaderWriterLockSlim以及因此想要阻止theard這麼多(改善性能)

+0

+1提醒我關於ReaderWriterLocks,並且有一個瘦身版本。我完全忘記了這些,因爲我不得不擔心線程問題已經有幾年了。將@flq標記爲接受的答案,因爲我將使用他引用的ConcurrentDictionary。 – 2010-09-29 13:20:14

+0

這很好 - 我以前不知道ConcurrentDictionary ;-)。你每天都會學到...... – 2010-09-29 13:35:10

+1

我*高度*懷疑在這種情況下'ReaderWriterLockSlim'實際上比普通的舊'lock'慢。 – 2010-09-29 13:45:43

0

是,_containers是因爲它的作用範圍一樣的字典實例,從理論上確定這是一個靜態的(顯然 - 這是字典實例)。

但是,一旦有人擔心你的整體設計是你說這是託管在一個網站。每個asp.net worker都是一個獨立的進程,因此當IIS由於閒置或其他原因而循環工作進程時,您的靜態字典可能會被銷燬。此外,您還可以,如果你正在使用Web園有你的字典的多個實例

+0

PERFMON計數器保持活着,只要至少有一個實例。在內存中活動的.NET引用。但即使在內存中存在兩個+ .NET引用,它們仍然會增加PERFMON計數器的單個實例。因此,同一臺服務器上的多個IIS站點將按我的需要工作。網絡農場也很好,因爲我想在服務器基礎上測量服務器。但謝謝你的提問。 – 2010-09-29 13:26:26

5

不要與ConcurrentDictionary使用的建議不以爲然,但更普遍的回答(多個工人每個應用程序。):

  1. 最好的鎖定解決方案是根本不鎖定的解決方案。有時候,不鎖定的情況下併發併發並不困難。其次最好的是擁有細密的鎖。然而,粗粒鎖更容易推理,因此對其正確性有信心。除非你有一個經過良好測試的無鎖類別的合適目的,否則從粗粒度鎖定開始(使用相同鎖定阻止一堆操作的鎖定),然後移動到更細粒度,因爲存在一個邏輯上的死鎖(你可以看到兩個不相關的操作可能會彼此阻塞),或者如果需要優化。
  2. 永遠不要鎖定在this因爲你的代碼外部的東西可以鎖定對象,你可以很容易地得到一個死鎖或至少鎖定爭奪,只能通過查看類內部的代碼和調用代碼(以及寫人的人可能無法訪問其他人)。由於類似的原因,永遠不要鎖定Type
  3. 由於類似的原因,只鎖定私人會員。這樣,只有類內部的代碼才能鎖定它,鎖爭用的可能性僅限於該地點。
  4. 避免將partial與此類代碼一起使用,並且如果您確實使用partial,請嘗試將對象上的所有鎖保留在同一位置。這裏沒有技術上的原因,但是當你需要考慮所有可能發生鎖定的地方時,它確實有幫助。
  5. 永遠不要鎖定值類型(整數類型,結構等)。這樣做會將值填入新對象並鎖定。然後,當其他代碼嘗試獲取鎖定時,它會將其鎖定到另一個對象並對其進行鎖定。基本上根本沒有鎖(除了理論上的優化,拳擊使用flyweight模式[它沒有],但實際上會讓事情變得更糟)。
  6. 有時,鎖的用途將與使用鎖時使用的單個對象相關,並且在不使用鎖時不使用。在這種情況下,將該對象用作鎖定有助於代碼的可讀性,從而將鎖與該對象相關聯。
  7. 擁有一個純粹用於鎖定目的的對象永遠不會錯,所以如果你不確定,就這樣做。
  8. 如果任何受影響的對象是靜態的,則鎖定對象必須是靜態的,並且可能是其他情況。相反,由於實例鎖比靜態鎖更精細,因此實例在適用時更好。
+0

感謝您的詳細解答,忘記了如果您鎖定值類型會發生的拳擊。 – 2010-09-29 17:26:01

+0

+1了不起的信息喬恩,希望我最喜歡的答案:-) – Myster 2011-02-18 00:33:43

相關問題