concurrentqueue天然线程安全,所有公开方法无需加锁;其getenumerator()不保证快照一致性,禁止foreach或toarray();trydequeue/trypeek返回值仅表示本次cas是否成功,不可反推队列状态;count为o(n)且不准,不应用于循环或业务判断。

ConcurrentQueueEnqueue、TryDequeue、TryPeek)无需加 lock,加锁反而破坏性能、可能引入死锁。
为什么不能用 foreach 或 ToArray() 遍历 ConcurrentQueue
它的 GetEnumerator() 不保证快照一致性:遍历时可能看到部分入队元素、漏掉刚入队的、甚至抛 InvalidOperationException(“集合已被修改”)。这不是 bug,是为无锁高吞吐做的权衡。
- 永远不要写
foreach (var item in queue)或queue.ToArray()用于业务逻辑 - 需要“快照式读取”时,应改用
BlockingCollection<t></t>,或显式加锁 + 普通Queue<t></t> - 真要遍历全部元素并消费,用
while (queue.TryDequeue(out var item)) { /* 处理 */ },而不是靠Count控制循环
TryDequeue 和 TryPeek 的返回值不是“队列是否为空”的可靠信号
TryDequeue(out T) 和 TryPeek(out T) 返回 bool,只表示“本次 CAS 操作是否成功”,不反映队列整体状态。内存压力、GC 暂停、多核缓存不一致都可能导致失败,即使队列非空。
- 错误写法:
if (!queue.TryDequeue(out _)) { /* 认为队列空 */ } - 正确姿势:只用返回值决定“本次是否拿到有效数据”,不反推状态
- 判断空应优先用
IsEmpty(比Count == 0快),但仍仅作瞬时参考,不可用于同步逻辑
Count 属性为什么又慢又不准
Count 是 O(n) 操作:它需遍历内部链表分段(segment)求和;且遍历过程中其他线程可能正在 Enqueue 或 TryDequeue,结果瞬间过期。
- 别在循环条件里写
while (queue.Count > 0)—— 可能漏数据或无限循环 - 别用
Count做业务分支,比如 “如果超过 100 个就告警”,应改用原子计数器(Interlocked)或监控采样 -
Count适合调试打印、Prometheus 指标上报等低频只读场景
最常被忽略的其实是把 IsEmpty 或 Count 当成同步原语——它们不带内存屏障,也不阻塞,只是某条 CPU 核心上那一刻的寄存器快照。











