ConcurrentDictionaryTKey, TValue是 .NET 中一个线程安全的字典集合专为高并发读写场景设计。它是System.Collections.Concurrent命名空间下的核心类型之一适用于多线程环境中需要高效、安全地共享键值对数据的场景。✅ 一、为什么需要ConcurrentDictionary普通DictionaryTKey, TValue不是线程安全的。如果多个线程同时读写1234var dict newDictionarystring,int();// 线程A: dict[a] 1;// 线程B: dict[b] 2;// 可能抛出 InvalidOperationException 或数据损坏即使加锁lock也能实现线程安全但会带来性能瓶颈串行化访问。而ConcurrentDictionary无需外部加锁内部使用细粒度锁或无锁算法支持高并发读 适度并发写 二、核心特性特性说明线程安全所有公共成员Add、Get、Remove 等都是线程安全的高性能并发读读操作几乎无锁lock-free性能接近普通字典分段/桶式结构内部将数据分片buckets减少写冲突原子操作支持提供AddOrUpdate,GetOrAdd等复合原子操作不保证顺序和Dictionary一样不维护插入顺序⚠️ 注意ConcurrentDictionary 的枚举foreach是线程安全的快照但可能包含“过时”数据因为其他线程可能正在修改。 三、常用 API 与示例1. 创建123var cache newConcurrentDictionarystring,int();// 或指定初始容量和并发级别高级用法var cache2 newConcurrentDictionarystring,int(concurrencyLevel: 4, capacity: 16);2. 基本操作线程安全1234567891011121314// 添加如果不存在cache.TryAdd(key1, 100);// 返回 bool// 获取如果存在if(cache.TryGetValue(key1,outintvalue)){Console.WriteLine(value);// 100}// 更新如果存在cache.TryUpdate(key1, 200, 100);// 仅当当前值为100时更新为200// 删除cache.TryRemove(key1,outintremovedValue);3. 高级原子操作⭐ 最常用✅GetOrAdd(key, valueFactory)如果 key 不存在则调用工厂方法创建值并添加否则返回现有值。123456var config cache.GetOrAdd(config, key {// 模拟耗时加载只执行一次Thread.Sleep(1000);returnLoadConfigFromDatabase();}); 多个线程同时调用 GetOrAdd(config, ...) 时工厂方法只会被调用一次其他线程等待结果避免重复初始化✅AddOrUpdate(key, addValueFactory, updateValueFactory)如果不存在则添加存在则更新。12345// 实现计数器cache.AddOrUpdate(counter,addValue: 1,// 不存在时设为1updateValueFactory: (key, oldValue) oldValue 1// 存在时1);⚖️ 四、与加锁Dictionary的性能对比场景Dictionary lockConcurrentDictionary高并发读所有读需排队慢几乎无锁快低并发写串行写中等分段锁较快高并发写严重瓶颈仍优于全局锁代码简洁性需手动管理锁无需锁API 更丰富 在典型 Web 应用缓存场景大量读 少量写ConcurrentDictionary 性能可提升 5~10 倍。 五、常见误区❌ 误区 1认为dict[key] value是原子的1234// 错误这实际上是// if (exists) update; else add;// 但中间可能被其他线程干扰cache[key] newValue;// 不是原子操作✅ 正确做法1cache.AddOrUpdate(key, newValue, (k, old) newValue);❌ 误区 2在GetOrAdd中做非幂等操作123456789// 危险工厂方法可能被多次调用虽然最终只存一个结果var obj cache.GetOrAdd(key, k newExpensiveObject());// OK// 更危险有副作用的操作cache.GetOrAdd(key, k {Log(Creating instance);// 可能被记录多次returnnewMyService();}); 虽然最终值是唯一的但工厂方法可能被多个线程同时调用.NET 6 已优化为单次调用但旧版本不一定。建议工厂方法无副作用、幂等。 六、典型应用场景1.内存缓存Cache123456789publicclassInMemoryCache{privatereadonlyConcurrentDictionarystring,object _cache new();publicT GetOrCreateT(stringkey, FuncT factory){return(T)_cache.GetOrAdd(key, k factory());}}2.计数器 / 统计123456privatereadonlyConcurrentDictionarystring,int _hitCounts new();publicvoidRecordHit(stringpage){_hitCounts.AddOrUpdate(page, 1, (k, v) v 1);}3.对象池Object Pool1234567privatereadonlyConcurrentDictionaryType, Stackobject _pools new();publicobjectRent(Type type){var pool _pools.GetOrAdd(type, t newStackobject());returnpool.TryPop(outvar obj) ? obj : Activator.CreateInstance(type);} 七、性能调优建议参数说明concurrencyLevel预期并发更新线程数默认为 CPU 核心数capacity初始容量避免频繁扩容12345// 预期 8 个线程并发写初始存 1000 项var dict newConcurrentDictionarystring, Data(concurrencyLevel: 8,capacity: 1000); 大多数场景用默认构造函数即可除非你有明确的性能测试数据。✅ 总结何时使用ConcurrentDictionary场景推荐多线程读写共享字典✅ 强烈推荐高频读 低频写如缓存✅ 最佳选择需要原子“获取或创建”语义✅ 必选单线程或只读场景❌ 用普通 Dictionary 更轻量需要保持插入顺序❌ 考虑 ImmutableDictionary 或加锁的 SortedDictionary 记住ConcurrentDictionary 不是万能的但它是在并发字典场景下最高效、最安全的选择。