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

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


















