当前位置: 首页 > news >正文

C# ConcurrentDictionary的使用小结

ConcurrentDictionary<TKey, TValue>是 .NET 中一个线程安全的字典集合,专为高并发读写场景设计。它是System.Collections.Concurrent命名空间下的核心类型之一,适用于多线程环境中需要高效、安全地共享键值对数据的场景。

✅ 一、为什么需要ConcurrentDictionary?

普通Dictionary<TKey, TValue>不是线程安全的。如果多个线程同时读写:

1

2

3

4

var dict =newDictionary<string,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. 创建

1

2

3

var cache =newConcurrentDictionary<string,int>();

// 或指定初始容量和并发级别(高级用法)

var cache2 =newConcurrentDictionary<string,int>(concurrencyLevel: 4, capacity: 16);

2. 基本操作(线程安全)

1

2

3

4

5

6

7

8

9

10

11

12

13

14

// 添加(如果不存在)

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 不存在,则调用工厂方法创建值并添加;否则返回现有值。

1

2

3

4

5

6

var config = cache.GetOrAdd("config", key =>

{

// 模拟耗时加载(只执行一次!)

Thread.Sleep(1000);

returnLoadConfigFromDatabase();

});

💡 多个线程同时调用 GetOrAdd("config", ...) 时,工厂方法只会被调用一次(其他线程等待结果),避免重复初始化!

✅AddOrUpdate(key, addValueFactory, updateValueFactory)

如果不存在则添加,存在则更新。

1

2

3

4

5

// 实现计数器

cache.AddOrUpdate("counter",

addValue: 1,// 不存在时设为1

updateValueFactory: (key, oldValue) => oldValue + 1// 存在时+1

);

⚖️ 四、与加锁Dictionary的性能对比

场景Dictionary + lockConcurrentDictionary
高并发读所有读需排队(慢)几乎无锁(快)
低并发写串行写(中等)分段锁(较快)
高并发写严重瓶颈仍优于全局锁
代码简洁性需手动管理锁无需锁,API 更丰富

📊 在典型 Web 应用缓存场景(大量读 + 少量写),ConcurrentDictionary 性能可提升 5~10 倍。

🚫 五、常见误区

❌ 误区 1:认为dict[key] = value是原子的

1

2

3

4

// 错误!这实际上是:

// if (exists) update; else add;

// 但中间可能被其他线程干扰

cache["key"] = newValue;// 不是原子操作!

✅ 正确做法:

1

cache.AddOrUpdate("key", newValue, (k, old) => newValue);

❌ 误区 2:在GetOrAdd中做非幂等操作

1

2

3

4

5

6

7

8

9

// 危险!工厂方法可能被多次调用(虽然最终只存一个结果)

var obj = cache.GetOrAdd("key", k =>newExpensiveObject());// OK

// 更危险:有副作用的操作

cache.GetOrAdd("key", k =>

{

Log("Creating instance");// 可能被记录多次!

returnnewMyService();

});

💡 虽然最终值是唯一的,但工厂方法可能被多个线程同时调用(.NET 6+ 已优化为单次调用,但旧版本不一定)。建议工厂方法无副作用、幂等

🛠 六、典型应用场景

1.内存缓存(Cache)

1

2

3

4

5

6

7

8

9

publicclassInMemoryCache

{

privatereadonlyConcurrentDictionary<string,object> _cache =new();

publicT GetOrCreate<T>(stringkey, Func<T> factory)

{

return(T)_cache.GetOrAdd(key, k => factory());

}

}

2.计数器 / 统计

1

2

3

4

5

6

privatereadonlyConcurrentDictionary<string,int> _hitCounts =new();

publicvoidRecordHit(stringpage)

{

_hitCounts.AddOrUpdate(page, 1, (k, v) => v + 1);

}

3.对象池(Object Pool)

1

2

3

4

5

6

7

privatereadonlyConcurrentDictionary<Type, Stack<object>> _pools =new();

publicobjectRent(Type type)

{

var pool = _pools.GetOrAdd(type, t =>newStack<object>());

returnpool.TryPop(outvar obj) ? obj : Activator.CreateInstance(type);

}

📏 七、性能调优建议

参数说明
concurrencyLevel预期并发更新线程数(默认为 CPU 核心数)
capacity初始容量(避免频繁扩容)

1

2

3

4

5

// 预期 8 个线程并发写,初始存 1000 项

var dict =newConcurrentDictionary<string, Data>(

concurrencyLevel: 8,

capacity: 1000

);

💡 大多数场景用默认构造函数即可,除非你有明确的性能测试数据。

✅ 总结:何时使用ConcurrentDictionary?

场景推荐
多线程读写共享字典✅ 强烈推荐
高频读 + 低频写(如缓存)✅ 最佳选择
需要原子“获取或创建”语义✅ 必选
单线程或只读场景❌ 用普通 Dictionary 更轻量
需要保持插入顺序❌ 考虑 ImmutableDictionary 或加锁的 SortedDictionary

🔑 记住:ConcurrentDictionary 不是万能的,但它是在并发字典场景下最高效、最安全的选择。

http://www.jsqmd.com/news/875627/

相关文章:

  • 2026石家庄市黄金回收门店指南:黄金 白银 铂金 彩金回收五家门店实测及联系方式推荐 - 盛世金银回收
  • 告别网盘!用Windows自带的IIS和cpolar,5分钟搭建一个私人WebDAV文件服务器
  • PGP 8.0.2在Windows 10兼容性安装全指南
  • 2026淮北市黄金回收门店指南:黄金 白银 铂金 彩金回收五家门店实测及联系方式推荐 - 盛世金银回收
  • 量子神经网络在医疗风险预测中的优化与应用
  • 2026内江市黄金回收门店指南:黄金 白银 铂金 彩金回收五家门店实测及联系方式推荐 - 盛世金银回收
  • vC#控制反转的使用详解
  • C盘空间告急?别急着删pagefile.sys,先搞懂Windows虚拟内存怎么设置才不卡
  • 2026石嘴山市黄金回收门店指南:黄金 白银 铂金 彩金回收五家门店实测及联系方式推荐 - 盛世金银回收
  • 2026海口市黄金回收门店指南:黄金 白银 铂金 彩金回收五家门店实测及联系方式推荐 - 盛世金银回收
  • 2026淮南市黄金回收门店指南:黄金 白银 铂金 彩金回收五家门店实测及联系方式推荐 - 盛世金银回收
  • 2026巴彦淖尔市黄金回收门店指南:黄金 白银 铂金 彩金回收五家门店实测及联系方式推荐 - 盛世金银回收
  • Go JWT实战:从iOS兼容性到双存储Refresh Token的完整落地
  • 从文本到流程:NLP与LLM驱动的业务流程模型自动提取技术
  • 2026邯郸市黄金回收门店指南:黄金 白银 铂金 彩金回收五家门店实测及联系方式推荐 - 盛世金银回收
  • 嵌入式信号函数时序模拟与µVision调试技巧
  • 2026宁波市黄金回收门店指南:黄金 白银 铂金 彩金回收五家门店实测及联系方式推荐 - 盛世金银回收
  • 2026朔州市黄金回收门店指南:黄金 白银 铂金 彩金回收五家门店实测及联系方式推荐 - 盛世金银回收
  • 2026巴中市黄金回收门店指南:黄金 白银 铂金 彩金回收五家门店实测及联系方式推荐 - 盛世金银回收
  • 2026黄冈市黄金回收门店指南:黄金 白银 铂金 彩金回收五家门店实测及联系方式推荐 - 盛世金银回收
  • 2026宁德市黄金回收门店指南:黄金 白银 铂金 彩金回收五家门店实测及联系方式推荐 - 盛世金银回收
  • 2026杭州市黄金回收门店指南:黄金 白银 铂金 彩金回收五家门店实测及联系方式推荐 - 盛世金银回收
  • 深入Linux显示架构:从一次AnolisOS黑屏事件,看懂xrandr、Xorg、GDM与显示器EDID是如何协同工作的
  • 2026四平市黄金回收门店指南:黄金 白银 铂金 彩金回收五家门店实测及联系方式推荐 - 盛世金银回收
  • 极验4滑块验证码纯Python逆向实现与工程化落地
  • 2026常德市黄金回收门店指南:黄金 白银 铂金 彩金回收五家门店实测及联系方式推荐 - 盛世金银回收
  • 2026白城市黄金回收门店指南:黄金 白银 铂金 彩金回收五家门店实测及联系方式推荐 - 盛世金银回收
  • 2026攀枝花市黄金回收门店指南:黄金 白银 铂金 彩金回收五家门店实测及联系方式推荐 - 盛世金银回收
  • 2026黄山市黄金回收门店指南:黄金 白银 铂金 彩金回收五家门店实测及联系方式推荐 - 盛世金银回收
  • K-12机器学习教育:数据驱动与算法驱动的教学路径选择与融合