ConcurrentDictionary 在多线程读写场景下性能显著优于加锁 Dictionary:写入快 2~3 倍(分段锁/CAS),读取快 2~5 倍(完全无锁),且避免漏锁、死锁和性能瓶颈等常见问题。

ConcurrentDictionary 写入比加锁 Dictionary 快得多
在多线程写入场景下,ConcurrentDictionary 的 TryAdd 或索引赋值(如 dict[i] = value)天然线程安全,内部采用分段锁(segment-based locking)或无锁 CAS 操作;而手动对 Dictionary 加 lock 会导致所有线程串行排队——哪怕只是往不同 key 写入,也必须等前一个 lock 释放。
- 实测 100 万次并行写入(
Parallel.For),ConcurrentDictionary耗时通常只有加锁Dictionary的 1/3~1/2 - 锁粒度越粗,竞争越激烈:单个
lock(obj)是全局锁,CPU 核数越多,性能下降越明显 -
ConcurrentDictionary默认按 CPU 核数分片(如 4 核 → 4 个 segment 锁),不同 key 可能落在不同 segment,真正实现并发写入
读取性能差距更明显,尤其高并发只读场景
这是最容易被低估的一点:ConcurrentDictionary 的读操作(如 TryGetValue、索引器 dict[key])完全无锁;而加锁 Dictionary 的读也要进 lock 块——哪怕只是查一个 key,也会阻塞其他读/写线程。
- 100 万次并行读取,
ConcurrentDictionary通常比加锁Dictionary快 2~5 倍 - 如果你的场景是“大量线程频繁读、偶尔写”,
ConcurrentDictionary几乎零成本;而ReaderWriterLockSlim+Dictionary虽可优化,但代码复杂、易出错、且仍不如原生ConcurrentDictionary简洁高效 - 注意:
foreach枚举ConcurrentDictionary是线程安全快照,但不保证实时性——这点和加锁遍历Dictionary行为一致,不是缺陷,是设计取舍
别用 Dictionary + lock 替代 ConcurrentDictionary 的 3 个典型坑
很多老项目沿用 Dictionary 加锁,看似可控,实际埋雷:
-
漏锁:新增方法忘了加
lock,或在异步回调中访问未同步的Dictionary,直接抛InvalidOperationException或数据损坏 - 死锁风险:多个 lock 对象嵌套顺序不一致,或在 lock 中调用未知第三方代码(可能又持另一把锁)
-
性能假象:单线程测试看不出问题,一上压测环境,
lock成为瓶颈,CPU 利用率低但吞吐卡死——此时换ConcurrentDictionary往往立竿见影
var concurrentDict = new ConcurrentDictionary<int, string>();
var dict = new Dictionary<int, string>();
var locker = new object();
// ✅ 推荐:简洁、安全、高性能
Parallel.For(0, 100000, i =>
{
concurrentDict[i] = $"val-{i}";
});
// ❌ 风险高:锁住整个字典,所有线程争抢同一把锁
Parallel.For(0, 100000, i =>
{
lock (locker)
{
dict[i] = $"val-{i}";
}
});
真正需要纠结的不是“该不该用 ConcurrentDictionary”,而是“有没有必要为了省几毫秒单线程开销,承担多线程下的不确定性、维护成本和潜在崩溃”。除非你明确运行在单线程上下文(比如初始化阶段),否则默认选 ConcurrentDictionary 更省心——它不是银弹,但确实是 .NET 并发字典场景下最平衡、最不易出错的选择。











