回答

0

詞典收藏的好處是快速關鍵訪問和快速包含評估。 所有字典都使用不同的複雜算法(請查看one),從零開始編寫自己的代碼將非常困難。

所以我試圖原ConcurrentDictionary的重用源代碼BCL(你可以get source code每一個基類)。 但是,它非常複雜,並且使用數十個私有類和低級函數來管理內存。這不是偶然的,它從一開始就不在PCL中提供:)

第二個想法是製作普通字典的包裝併爲每個方法調用添加鎖定部分。儘管關鍵部分並不昂貴,但卻導致性能顯着下降。

因此,我已經結束了清除字典<,>包裝的聯鎖實施。它發生的情況是,正常的字典支持從不同線程讀取和枚舉,只要它不在同一時間修改即可。 因此,每次更改我們都會克隆所有字典。在那段時間讀取它的所有線程將繼續迭代舊版本。

public class InterlockedDictionary<TKey, TValue> : IEnumerable<KeyValuePair<TKey, TValue>> 
{ 
    private Dictionary<TKey, TValue> _dictionary = new Dictionary<TKey, TValue>(); 


    public TValue this[TKey key] 
    { 
     get 
     { 
      return _dictionary[key]; 
     } 
     set 
     {     
      ApplyAndChange(dict => dict[key] = value); 
     } 
    } 

    public Dictionary<TKey, TValue>.KeyCollection Keys 
    { 
     get 
     { 
      return _dictionary.Keys; 
     } 
    } 

    public Dictionary<TKey, TValue>.ValueCollection Values 
    { 
     get 
     { 
      return _dictionary.Values; 
     } 
    } 

    public int Count 
    { 
     get 
     { 
      return _dictionary.Count; 
     } 
    } 


    public void Add(KeyValuePair<TKey, TValue> item) 
    { 
     ApplyAndChange(dict => dict.Add(item.Key, item.Value)); 
    } 

    public void Clear() 
    { 
     _dictionary = new Dictionary<TKey, TValue>(); 
    } 

    public bool ContainsKey(TKey key) 
    { 
     return _dictionary.ContainsKey(key); 
    } 

    public void Add(TKey key, TValue value) 
    { 
     ApplyAndChange(dict => dict.Add(key, value)); 
    } 

    public bool Remove(TKey key) 
    { 
     bool result = false; 
     ApplyAndChange(dict => result = dict.Remove(key)); 
     return result; 
    } 


    public IEnumerator<KeyValuePair<TKey, TValue>> GetEnumerator() 
    { 
     return _dictionary.GetEnumerator(); 
    } 

    IEnumerator IEnumerable.GetEnumerator() 
    { 
     return GetEnumerator(); 
    } 


    private void ApplyAndChange(Action<Dictionary<TKey, TValue>> action) 
    { 
     Dictionary<TKey, TValue> initialDictionary, updatedDictionary, replacedDictionary; 
     do 
     { 
      initialDictionary = _dictionary; 
      updatedDictionary = new Dictionary<TKey, TValue>(initialDictionary); 
      action(updatedDictionary); 
     } while (Interlocked.CompareExchange(ref _dictionary, updatedDictionary, initialDictionary) != initialDictionary); 
    } 
} 

此實現不會實現IDictionary <,因爲很多不是必需的成員。但是,通過直接調用所有非修改方法的底層字典並使用ApplyAndChange(dict => ...)包裝所有修改方法,可以輕鬆完成。

N.B. - 至於性能,與原始字典<相比,>此實現具有更差的設置,添加和刪除並等於讀取和包含性能。

+1

這將是線程安全的*如果*所有非互鎖操作都是原子操作,但它們不是。在你的索引器'set'中,'_dictionary'可以在'_dictionary.ContainsKey(key)'和'_dictionary [key] = value;'行之間改變。此解決方案不是線程安全的。 – dcastro

+0

我相信一個'ReaderWriterLockSlim'是一個更好的解決方案 - 你會得到相同的性能,但是使用更少的容易出錯的方法。無鎖碼很容易破壞。 – dcastro

+0

@dcastro - 謝謝,這確實是錯誤的,修正了它 –

1

如果你對閱讀更感興趣,你應該使用ReaderWriterLockSlim

這是基本鎖定,允許多個讀者或一個作家在任何給定時間進入臨界區。當字典正在被多個線程讀取時,這實際上會導致性能下降,並且僅在線程嘗試寫入時應用互斥。

你會實現它作爲這樣(我省略了大部分的方法爲簡潔 - 我離開Add作爲一個作家爲例,Contains作爲一個讀者爲例):

public class CustomDictionary<TKey, TValue> : IDictionary<TKey, TValue> 
{ 
    private readonly ReaderWriterLockSlim _lock = new ReaderWriterLockSlim(); 
    private readonly IDictionary<TKey, TValue> _dictionary = new Dictionary<TKey, TValue>(); 

    public bool Contains(KeyValuePair<TKey, TValue> item) 
    { 
     _lock.EnterReadLock(); 
     try 
     { 
      return _dictionary.Contains(item); 
     } 
     finally 
     { 
      _lock.EnterReadLock(); 
     } 
    } 

    public void Add(KeyValuePair<TKey, TValue> item) 
    { 
     _lock.EnterWriteLock(); 
     try 
     { 
      _dictionary.Add(item); 
     } 
     finally 
     { 
      _lock.ExitWriteLock(); 
     } 
    } 
} 

現在,GetEnumerator是有點棘手鎖,因爲字典實際上會在之後被讀取,調用GetEnumerator而不是,而它被調用。

所以,你應該實現自己的枚舉和應用讀鎖這樣,使鎖將於收集被列舉:

public IEnumerator<KeyValuePair<TKey, TValue>> GetEnumerator() 
    { 
     _lock.EnterReadLock(); 
     try 
     { 
      foreach (var entry in _dictionary) 
       yield return entry; 
     } 
     finally 
     { 
      _lock.ExitReadLock(); 
     } 
    }