ConcurrentDictionary 不能直接用 ContainsKey + this[key] 查值,因为两次调用间可能被其他线程修改,导致 KeyNotFoundException;应使用原子的 TryGetValue。

ConcurrentDictionary 为啥不能直接用 ContainsKey + this[key] 查值
因为两次调用之间可能被其他线程修改,ContainsKey 返回 true 后,this[key] 可能已不存在或值已变——这不是 bug,是并发下的正常竞态。正确做法是用 TryGetValue 一次完成判断和取值。
-
TryGetValue是原子操作,线程安全,推荐作为默认查值方式 - 避免写
if (dict.ContainsKey(k)) var v = dict[k];,这在高并发下会偶发KeyNotFoundException - 如果要“有则更新、无则添加”,优先用
AddOrUpdate,而不是先TryGetValue再手动TryAdd
ConcurrentQueue 的 TryDequeue 和 TryPeek 到底返回啥
两者都返回 bool:成功为 true,失败(队列空)为 false;值通过 out T result 参数传出。注意:它们不阻塞,也不抛异常,空队列时只是静默返回 false。
-
TryDequeue移除并返回队首元素;TryPeek仅查看不移除 - 永远检查返回值,不要假设
out参数一定被赋值(未成功时它可能是默认值) - 没有
Count属性的实时性保证——Count是快照值,多线程下可能立刻过期
什么时候该用 ConcurrentDictionary,而不是加锁的普通 Dictionary
不是“只要多线程就无脑换并发集合”。ConcurrentDictionary 内部用分段锁+无锁读,写入吞吐高,但单次操作开销比普通 Dictionary 大;且不支持枚举时修改(仍会抛 InvalidOperationException)。
- 读多写少、写操作分散(如缓存键值对、统计计数器),适合
ConcurrentDictionary - 写操作集中、或需强一致性遍历(比如边遍历边删),普通
Dictionary+lock更可控 -
ConcurrentDictionary的Keys/Values是快照,遍历时原字典可被修改,但快照本身不可变
ConcurrentQueue 在生产者-消费者模型里漏掉“空队列”处理会怎样
典型错误是写 while (queue.TryDequeue(out var item)) { Process(item); },以为能清空——其实这是错的:多个线程同时消费时,某次 TryDequeue 失败后,其他线程可能刚入队新项,而当前线程已退出循环,导致项滞留。
- 正确模式是外层用
while (true)或结合信号(如BlockingCollection)控制生命周期 - 单独用
ConcurrentQueue时不提供阻塞能力,需要自己配合SpinWait或事件通知 - 若需阻塞取值,直接上
BlockingCollection<T>(底层包装ConcurrentQueue),它提供Take和带超时的TryTake
并发集合不是银弹,ConcurrentDictionary 的分段锁粒度、ConcurrentQueue 的无锁实现细节,都会在极端压力下暴露行为偏差。最常被忽略的是:它们只保单个操作线程安全,组合逻辑(比如“查+删+记录日志”)仍需额外同步。


















