You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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);
        }
    }
}

关键注意点

  1. 序列化处理:锁对象标记为[NonSerialized],序列化仅保存数据部分,反序列化时重新初始化锁。
  2. 枚举安全:返回数据快照而非直接枚举原字典,避免枚举过程中字典被修改引发异常。
  3. 性能权衡:方案1实现简单、易维护,适合大多数场景;方案2并发性能更高,但代码复杂度提升,需根据实际需求选择。

内容的提问来源于stack exchange,提问作者MrBott_a

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.14 10:55:27