將我的代碼移動到可移植框架中,我發現在Portable Class Library(PCL)中沒有可用的併發集合,這個discussion很好地解釋了它。Portable .NET Framework中併發集合的模擬
但我仍然需要並行字典模擬在那裏運行。 我對讀取/包含性能比對修改性能更感興趣。
將我的代碼移動到可移植框架中,我發現在Portable Class Library(PCL)中沒有可用的併發集合,這個discussion很好地解釋了它。Portable .NET Framework中併發集合的模擬
但我仍然需要並行字典模擬在那裏運行。 我對讀取/包含性能比對修改性能更感興趣。
詞典收藏的好處是快速關鍵訪問和快速包含評估。 所有字典都使用不同的複雜算法(請查看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. - 至於性能,與原始字典<相比,>此實現具有更差的設置,添加和刪除並等於讀取和包含性能。
如果你對閱讀更感興趣,你應該使用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();
}
}
這將是線程安全的*如果*所有非互鎖操作都是原子操作,但它們不是。在你的索引器'set'中,'_dictionary'可以在'_dictionary.ContainsKey(key)'和'_dictionary [key] = value;'行之間改變。此解決方案不是線程安全的。 – dcastro
我相信一個'ReaderWriterLockSlim'是一個更好的解決方案 - 你會得到相同的性能,但是使用更少的容易出錯的方法。無鎖碼很容易破壞。 – dcastro
@dcastro - 謝謝,這確實是錯誤的,修正了它 –