贝利信息

c# 在高并发下,如何选择合适的Concurrent集合

日期:2026-01-20 00:00 / 作者:星降
Concur

rentDictionary适用于读多写少且需键值查找的高并发场景,支持分段锁和原子操作;ConcurrentQueue/Stack适用于生产者-消费者模型;BlockingCollection封装并发集合提供阻塞能力;ConcurrentBag仅适合线程本地操作。

ConcurrentDictionary 适合「读多写少 + 键值查找」场景

高并发下频繁按 key 查找、偶尔增删时,ConcurrentDictionary 是首选。它内部用分段锁(默认 31 段),比全局锁的 Dictionary + lock 吞吐高得多,且线程安全地支持 GetOrAddAddOrUpdate 等原子操作。

注意点:

ConcurrentQueue 和 ConcurrentStack 用于「生产者-消费者」解耦

当多个线程持续入队/压栈,另一批线程持续出队/弹栈(比如任务分发、日志缓冲),优先选 ConcurrentQueue(FIFO)或 ConcurrentStack(LIFO)。它们无锁实现,仅靠 CAS 和内存屏障,吞吐远高于加锁的 Queue/Stack

典型误用:

BlockingCollection 包装并发集合,解决「等待-唤醒」问题

BlockingCollection 本身不是集合,而是对 IProducerConsumerCollection(如 ConcurrentQueue)的封装,提供 Take 阻塞等待、GetConsumingEnumerable 持续消费等能力。它让生产者-消费者模型变得直观,避免手写 Monitor.Wait/Pulse

关键配置项:

别踩坑:ConcurrentBag 不等于「万能 List 替代品」

ConcurrentBag 在「每个线程主要操作自己添加的元素」时性能最优(线程本地存储 + 全局共享池),但它的设计牺牲了确定性:

简单说:只在明确知道「添加和消费基本由同一线程完成」(如异步任务收集中间结果)时才用 ConcurrentBag;否则优先考虑 ConcurrentQueueConcurrentDictionary

真正难处理的是混合操作——比如既要按 key 查,又要按插入顺序遍历,还要支持并发增删。这时候没有银弹,得拆成多个并发集合协作,或者引入更重的方案(如 ReaderWriterLockSlim + 普通集合),但务必实测压测验证瓶颈点在哪里。