2013-01-31 19 views
2

我正在爲C#編寫一個併發字典實現,我想知道這個GetEnumerator()實現實際上是(線程)安全爲併發字典暴露GetEnumerator()

它沒有做一個實際的快照,所以我想知道它是否會搞亂後來讀/寫到內部字典,或者它是否會暴露潛在的死鎖,因爲暴露的IEnumerator枚舉實際上會在裏面運行鎖。

private readonly Dictionary<TKey, TValue> internalDictionary; 
private SpinLock spinLock = new SpinLock(); 

IEnumerator IEnumerable.GetEnumerator() 
{ 
    IEnumerator enumerator; 

    bool lockTaken = false; 
    try 
    { 
     spinLock.TryEnter(ref lockTaken); 
     enumerator = (this.internalDictionary as IEnumerable).GetEnumerator(); 
    } 
    finally 
    { 
     if (lockTaken) 
     { 
      spinLock.Exit(false); 
     } 
    } 

    return enumerator; 
} 
+0

「snapsnot」我希望這是一個錯字... – antonijn

+1

是的,但他們明確建議你不要編輯單字母拼寫錯誤。 – antonijn

+0

這裏沒有具體的問題或疑問...... – asawyer

回答

1

你的方法不是線程安全對於因爲

  1. 您枚舉沒有任何快照併發作家。它是指原來的字典。請撥打ToList或其他東西來實際拍攝。
  2. 併發寫入器不使用您的鎖定,因此它們併發執行。這是不安全的。
  3. 如果鎖體很大,不要使用自旋鎖。
  4. 如果TryEnter失敗會怎麼樣?畢竟,它被稱爲嘗試

這裏是一個固定的版本,去掉了所有的小聰明:

IEnumerator IEnumerable.GetEnumerator() 
{ 
    lock (internalDictionary) return internalDictionary.ToList(); 
} 

併發作家必須採取鎖了。

+0

是的,但是* do *返回一個快照;)至於3和4,這實際上是由「lockTaken」覆蓋的。 –

+0

不確定你在說什麼。調用GetEnumerator不會快照字典的內容。 – usr

+0

是的,這是我的觀點。我在問題中提到我意識到它沒有拍攝快照。 –

1

是彈簧想到的第一件事情就是在地球上,你會想創建這樣一個東西自己當.NET附帶線程安全的併發集合類(包括字典)。 Google System.Collections.Concurrent瞭解更多信息。

我以前對ConcurrentDictionary類進行了基準測試,我會向你保證,即使在使用自旋鎖或互鎖來避免任何較重的鎖定機制時,它們的速度都比你自己寫的任何東西都快。

在回答你的問題,也許,但它取決於實施(這從來不是一件好事)。我猜測任何寫入嘗試都會失敗,因爲標準集合類在迭代時不能被修改;但我不會依賴任何這樣的機制來保證我自己的代碼安全。

+1

對於* why而言,顯而易見的答案是ConcurrentDictionary在所有.NET平臺上都不受支持。至於答案,這是一個合理的理由,根本不公開一個枚舉器,而只是通過提供快照。常規鍵/值屬性。 –

+0

@克勞斯的確。我建議研究ConcurrentDictionary的API表面,即使你不能使用該實現,因爲我相信MS已經考慮過相同的問題。也許你可以使用C5?這是一個非常全面和非常好的線程安全的數據結構庫(集合,樹等)。有關詳細信息,請參見http://www.itu.dk/research/c5/。 –

+0

@ClausJørgensen它看起來像C5集合默認情況下也不是線程安全的。相反,我可以爲您提供一個使用Interlocked的線程安全字典的實現,這是我爲Fasterflect庫編寫的。代碼可以在http://fasterflect.codeplex.com/SourceControl/changeset/view/41eae592599b#Fasterflect/Fasterflect/Caching/Cache.cs找到(向下滾動到#elif DOT_NET_35行)。它可能沒有你所需要的,但提供了一個體面的起點。此外,它使用互鎖,因此速度相當快。 –