应使用泛型 Queue 初始化,避免非泛型 Queue;多线程场景优先用 ConcurrentQueue;禁用裸 Dequeue,改用 TryDequeue;勿用 List 模拟队列,性能差50倍以上。

Queue 初始化时别用 new Queue() 而不是 new Queue()
泛型类型擦除在 C# 里不存在,但编译器会报错:如果你写 new Queue(),它根本过不了编译——因为非泛型 Queue 是老式类,属于 System.Collections,和泛型 Queue(System.Collections.Generic)是两个东西。混用会导致类型不安全、装箱拆箱开销,甚至运行时异常。
实操建议:
- 一律用
new Queue、() new Queue这种带泛型参数的写法() - 如果真要兼容旧代码或需要对象引用多态,才考虑
Queue(非泛型),但必须手动Cast或遍历转型,不推荐() - VS 提示 “'Queue' is ambiguous”?说明你同时引用了
System.Collections和System.Collections.Generic,删掉前者 using 更干净
Enqueue/Dequeue 的线程安全性问题很隐蔽
Queue 默认不是线程安全的。多个线程同时调用 Enqueue 或 Dequeue,哪怕只是读写分离(一个只进、一个只出),也可能因内部数组扩容导致 InvalidOperationException:“Collection was modified”。
常见错误现象:
- 偶尔抛出
InvalidOperationException,且无法稳定复现 - Dequeue 返回 null 或默认值,但 Count 显示还有元素
- 在 ASP.NET Core 后台任务中高频使用时出问题,本地调试却正常
实操建议:
- 高并发场景下,优先换
ConcurrentQueue,它的TryDequeue和TryEnqueue是原子操作,无锁设计 - 若必须用
Queue,加lock是最直白方案,但注意锁粒度:别在循环里反复 lock/unlock,应把一批操作包进一个 lock 块 - 不要依赖
Count > 0再Dequeue—— 中间可能被其他线程取走,改用TryDequeue(out T result)判断更可靠
Dequeue 抛出 InvalidOperationException 的真实原因
很多人以为 Dequeue 只在空队列时抛异常,其实还有更隐蔽的触发点:当队列底层数组正在扩容(比如从 4 扩到 8),而另一个线程恰好在读写,就可能破坏内部状态,导致 Dequeue 检查失败并抛出 InvalidOperationException。
使用场景:
- 后台定时任务轮询处理消息
- Unity 中帧回调里频繁进出队列
- WPF 数据绑定中直接暴露
Queue属性(绑定引擎会反复调用 Count/GetEnumerator)
实操建议:
- 永远不用裸
Dequeue(),改用if (queue.TryDequeue(out var item)) { /* use item */ } - 避免在 foreach 中直接遍历
Queue—— 它不支持边遍历边修改;如需清空,用while (queue.TryDequeue(out _)) - 初始化容量可预估:比如知道最多存 1000 条日志,就写
new Queue,减少扩容次数(1024)
Queue 和 List 在“模拟队列行为”时的性能陷阱
有人图省事,用 List 模拟出队,或者用 List + List + RemoveAt(0) 组合实现 FIFO。这在小数据量下看不出问题,但数据一多就明显卡顿。
性能影响:
-
List是 O(n) 操作:后面所有元素都要往前挪一位.RemoveAt(0) -
Queue是均摊 O(1),内部用循环数组,头尾指针移动即可.Dequeue() - 10 万条数据做 10 万次出队,List 方案可能比 Queue 慢 50 倍以上
实操建议:
- 只要语义是 FIFO,就用
Queue或ConcurrentQueue,别“看起来差不多”就换实现 - 需要随机访问(比如查第 5 个元素)?Queue 不支持,这时该用
List或LinkedList,但得接受它不是 FIFO 工具 - 调试时想看全部内容?别直接打印
queue——它没重写ToString(),用queue.ToList()临时转一下就行
Queue










