跳转到主内容
趣航编程网 - 趣学编程,启航技术之路!

C#中Queue队列详解_C#队列先进先出教程【核心】

C#中Queue是最轻量、最符合FIFO直觉的顺序处理容器,但不支持索引访问、非线程安全、空队列调用Dequeue()会抛异常;仅当需严格“先入先出、头取尾加”时才语义正确,如任务调度、打印排队、HTTP缓冲。 直接说结论:C# 中
Queue
是最轻量、最符合 FIFO 直觉的顺序处理容器,但不是万能的——它不支持索引访问、不能安全用于多线程默认场景、空队列调用
Dequeue()
会崩。 什么时候该用
Queue
而不是
List
或数组 当你明确需要「严格按插入顺序逐个取走、且只从头取、只从尾加」时,
Queue
才是语义正确的选择。比如任务调度器轮询、打印请求排队、HTTP 请求缓冲。
List
支持随机访问和中间插入,但用它模拟队列容易写出
list.RemoveAt(0)
这种 O(n) 操作,性能差且意图模糊 数组固定长度,扩容需手动处理;而
Queue
内部用循环数组实现,
Enqueue
平均时间复杂度是 O(1),扩容自动完成 如果只是遍历一次、不修改结构,用
foreach
遍历
Queue
安全;但别试图用
queue[i]
——它没这个索引器
Dequeue()
Peek()
的关键区别与风险点
Dequeue()
移除并返回队首元素;
Peek()
只读取不移除。二者都要求队列非空,否则抛出
InvalidOperationException
。 常见错误:在未检查
Count > 0
的情况下直接调用
Dequeue()
,尤其在多线程或异步回调中极易触发异常 安全写法不是靠 try-catch,而是先判断:
if (queue.Count > 0) { var item = queue.Dequeue(); ... }
Peek()
适合“预检”场景,比如日志队列中先看下下一条是什么再决定是否丢弃 注意:
Peek()
返回的是引用类型对象的引用,或值类型的副本;对引用类型成员的修改会反映在队列中原始对象上 初始化容量与性能影响
Queue
默认初始容量为 32,增长因子为 2.0。频繁扩容会影响吞吐,尤其在高吞吐生产者-消费者模型中。 C知道 CSDN推出的一款AI技术问答工具 下载 如果你能预估峰值大小(例如每秒入队 1000 条、处理延迟 ≤1s),建议显式指定容量:
new Queue(1024)
传入
IEnumerable
构造(如
new Queue(array)
)会一次性复制,内部容量 = 源集合长度,避免首次
Enqueue
就扩容 不要为了“省空间”设过小容量(如
new Queue(4)
),连续入队 5 次就会触发第一次扩容,反而增加拷贝开销 多线程场景下为什么不能直接用
Queue
Queue
本身不是线程安全的。多个线程同时
Enqueue
Dequeue
可能导致
Count
错乱、数据丢失甚至
NullReferenceException
。 别用
lock
包裹每次操作——虽然可行,但锁粒度太细,严重拖慢吞吐 正确替代是
ConcurrentQueue
,它提供无锁的
TryEnqueue
TryDequeue
,失败时返回
false
而非抛异常
ConcurrentQueue
不提供
Count
属性(因并发下统计无意义),改用
IsEmpty
判断更可靠 如果必须用普通
Queue
做跨线程传递,请确保由单一生产者 + 单一消费者协作,且通过信号机制(如
AutoResetEvent
)同步,而非共享访问 真正容易被忽略的点是:FIFO 不等于“顺序绝对可靠”。当业务逻辑依赖严格时间序或处理序时,得考虑
Enqueue
Dequeue
之间是否存在竞态、重试、丢弃等外部干扰——队列本身只保证内部操作顺序,不兜底业务语义。

相关文章