C#中用普通Dictionary模拟ConcurrentDictionary的实现方案(支持序列化)
用可序列化普通Dictionary模拟ConcurrentDictionary功能
ConcurrentDictionary的多线程核心机制
ConcurrentDictionary的核心是**分段锁(Bucket锁)**设计:它将内部数据拆分为多个独立的"桶"(Bucket),每个桶对应一把专属锁。当多线程操作不同桶内的键值对时,彼此不会阻塞,大幅提升并发性能;仅当操作同一桶内的数据时,才会触发锁竞争。此外它还结合了CAS(比较并交换)等无锁操作优化读性能,但核心并发保障依赖分段锁的隔离机制。
如果用普通Dictionary模拟,不需要完全复刻所有细节,只需实现核心的线程安全读写+可序列化特性即可。
具体实现方案
方案1:基于ReaderWriterLockSlim的简单实现(适合多数场景)
该方案用ReaderWriterLockSlim实现读写分离:读操作可并发执行,写操作互斥,兼顾线程安全与序列化需求,代码复杂度低。
using System; using System.Collections; using System.Collections.Generic; using System.Runtime.Serialization; [Serializable] public class SerializableConcurrentDictionary<TKey, TValue> : IDictionary<TKey, TValue>, ISerializable { private readonly Dictionary<TKey, TValue> _innerDict; [NonSerialized] private readonly ReaderWriterLockSlim _locker; public SerializableConcurrentDictionary() { _innerDict = new Dictionary<TKey, TValue>(); _locker = new ReaderWriterLockSlim(LockRecursionPolicy.NoRecursion); } // 反序列化构造函数 protected SerializableConcurrentDictionary(SerializationInfo info, StreamingContext context) { _innerDict = (Dictionary<TKey, TValue>)info.GetValue("Data", typeof(Dictionary<TKey, TValue>)); _locker = new ReaderWriterLockSlim(LockRecursionPolicy.NoRecursion); } // 实现序列化逻辑,仅保存数据字典 public void GetObjectData(SerializationInfo info, StreamingContext context) { _locker.EnterReadLock(); try { info.AddValue("Data", _innerDict); } finally { _locker.ExitReadLock(); } } // 核心线程安全方法:TryAdd public bool TryAdd(TKey key, TValue value) { _locker.EnterWriteLock(); try { if (_innerDict.ContainsKey(key)) return false; _innerDict.Add(key, value); return true; } finally { _locker.ExitWriteLock(); } } // 核心线程安全方法:TryGetValue public bool TryGetValue(TKey key, out TValue value) { _locker.EnterReadLock(); try { return _innerDict.TryGetValue(key, out value); } finally { _locker.ExitReadLock(); } } // 核心线程安全方法:TryRemove public bool TryRemove(TKey key, out TValue value) { _locker.EnterWriteLock(); try { if (_innerDict.TryGetValue(key, out value)) { _innerDict.Remove(key); return true; } value = default; return false; } finally { _locker.ExitWriteLock(); } } // 核心线程安全方法:TryUpdate public bool TryUpdate(TKey key, TValue newValue, TValue comparisonValue) { _locker.EnterWriteLock(); try { if (_innerDict.TryGetValue(key, out var existing) && EqualityComparer<TValue>.Default.Equals(existing, comparisonValue)) { _innerDict[key] = newValue; return true; } return false; } finally { _locker.ExitWriteLock(); } } // 实现IDictionary的其他成员(均需加锁) public TValue this[TKey key] { get { _locker.EnterReadLock(); try { return _innerDict[key]; } finally { _locker.ExitReadLock(); } } set { _locker.EnterWriteLock(); try { _innerDict[key] = value; } finally { _locker.ExitWriteLock(); } } } public ICollection<TKey> Keys { get { _locker.EnterReadLock(); try { return new List<TKey>(_innerDict.Keys); } finally { _locker.ExitReadLock(); } } } public ICollection<TValue> Values { get { _locker.EnterReadLock(); try { return new List<TValue>(_innerDict.Values); } finally { _locker.ExitReadLock(); } } } public int Count { get { _locker.EnterReadLock(); try { return _innerDict.Count; } finally { _locker.ExitReadLock(); } } } public bool IsReadOnly => false; public void Add(TKey key, TValue value) { _locker.EnterWriteLock(); try { _innerDict.Add(key, value); } finally { _locker.ExitWriteLock(); } } public void Add(KeyValuePair<TKey, TValue> item) => Add(item.Key, item.Value); public void Clear() { _locker.EnterWriteLock(); try { _innerDict.Clear(); } finally { _locker.ExitWriteLock(); } } public bool Contains(KeyValuePair<TKey, TValue> item) { _locker.EnterReadLock(); try { return ((ICollection<KeyValuePair<TKey, TValue>>)_innerDict).Contains(item); } finally { _locker.ExitReadLock(); } } public bool ContainsKey(TKey key) { _locker.EnterReadLock(); try { return _innerDict.ContainsKey(key); } finally { _locker.ExitReadLock(); } } public void CopyTo(KeyValuePair<TKey, TValue>[] array, int arrayIndex) { _locker.EnterReadLock(); try { ((ICollection<KeyValuePair<TKey, TValue>>)_innerDict).CopyTo(array, arrayIndex); } finally { _locker.ExitReadLock(); } } public IEnumerator<KeyValuePair<TKey, TValue>> GetEnumerator() { // 返回数据快照,避免枚举过程中字典被修改 var snapshot = new List<KeyValuePair<TKey, TValue>>(this); return snapshot.GetEnumerator(); } public bool Remove(TKey key) { _locker.EnterWriteLock(); try { return _innerDict.Remove(key); } finally { _locker.ExitWriteLock(); } } public bool Remove(KeyValuePair<TKey, TValue> item) { _locker.EnterWriteLock(); try { return ((ICollection<KeyValuePair<TKey, TValue>>)_innerDict).Remove(item); } finally { _locker.ExitWriteLock(); } } IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); }
方案2:分段锁实现(高并发场景)
如果需要更接近ConcurrentDictionary的并发性能,可以复刻分段锁逻辑:将内部数据拆分为多个子字典(桶),每个桶对应一把锁,操作时根据键的哈希值定位到对应桶,仅锁定该桶。
using System; using System.Collections.Generic; using System.Runtime.Serialization; [Serializable] public class SegmentedSerializableConcurrentDict<TKey, TValue> : ISerializable { private const int BucketCount = 16; // 可根据并发需求调整数量 private readonly Dictionary<TKey, TValue>[] _buckets; [NonSerialized] private readonly object[] _bucketLocks; public SegmentedSerializableConcurrentDict() { _buckets = new Dictionary<TKey, TValue>[BucketCount]; _bucketLocks = new object[BucketCount]; for (int i = 0; i < BucketCount; i++) { _buckets[i] = new Dictionary<TKey, TValue>(); _bucketLocks[i] = new object(); } } // 反序列化构造函数 protected SegmentedSerializableConcurrentDict(SerializationInfo info, StreamingContext context) { var mergedData = (Dictionary<TKey, TValue>)info.GetValue("MergedData", typeof(Dictionary<TKey, TValue>)); _buckets = new Dictionary<TKey, TValue>[BucketCount]; _bucketLocks = new object[BucketCount]; // 将合并后的数据重新分配到各个桶 for (int i = 0; i < BucketCount; i++) { _buckets[i] = new Dictionary<TKey, TValue>(); _bucketLocks[i] = new object(); } foreach (var kvp in mergedData) { int index = GetBucketIndex(kvp.Key); _buckets[index][kvp.Key] = kvp.Value; } } // 序列化时合并所有桶的数据 public void GetObjectData(SerializationInfo info, StreamingContext context) { var mergedData = new Dictionary<TKey, TValue>(); for (int i = 0; i < BucketCount; i++) { lock (_bucketLocks[i]) { foreach (var kvp in _buckets[i]) { mergedData[kvp.Key] = kvp.Value; } } } info.AddValue("MergedData", mergedData); } // 根据键的哈希值计算桶索引 private int GetBucketIndex(TKey key) { return Math.Abs(key.GetHashCode() % BucketCount); } // 线程安全的TryAdd public bool TryAdd(TKey key, TValue value) { int index = GetBucketIndex(key); lock (_bucketLocks[index]) { if (_buckets[index].ContainsKey(key)) return false; _buckets[index].Add(key, value); return true; } } // 其他核心方法(TryGetValue、TryRemove等)逻辑类似,仅锁定对应桶即可 public bool TryGetValue(TKey key, out TValue value) { int index = GetBucketIndex(key); lock (_bucketLocks[index]) { return _buckets[index].TryGetValue(key, out value); } } }
关键注意点
- 序列化处理:锁对象标记为
[NonSerialized],序列化仅保存数据部分,反序列化时重新初始化锁。 - 枚举安全:返回数据快照而非直接枚举原字典,避免枚举过程中字典被修改引发异常。
- 性能权衡:方案1实现简单、易维护,适合大多数场景;方案2并发性能更高,但代码复杂度提升,需根据实际需求选择。
内容的提问来源于stack exchange,提问作者MrBott_a
相关产品推荐
相关产品推荐

