引言:为什么 C# 中需要任务队列?
想象一下,您正在开发一个处理在线商店订单的应用程序。客户同时发送请求,每个请求都需要时间(例如,检查付款或发送电子邮件)。如果按顺序处理它们,应用程序会变慢,用户将不得不等待。解决方案是使用任务队列。
任务队列(Task Queue)是一种按照 FIFO(先进先出)原则工作的数据结构。在 C# (.NET) 中,队列有多种实现方式:从简单的 Queue<T> 到线程安全的 ConcurrentQueue<T>。在本文中,我们将介绍如何创建队列、添加和提取任务,以及如何在异步编程中使用它们。
本文面向初学者,因此我们将从基础开始,逐步过渡到更复杂的示例。
1. C# 中 Queue<T> 的基础操作
Queue<T> 类位于 System.Collections.Generic 命名空间中。这是一个简单的队列,不支持多线程——只能在单线程场景中使用。
创建队列和基本操作
让我们创建一个用于存储字符串任务的队列:
using System;using System.Collections.Generic;
class Program{ static void Main() { // 创建一个字符串队列 Queue<string> taskQueue = new Queue<string>();
// 添加任务 (Enqueue) taskQueue.Enqueue("发送邮件给客户"); taskQueue.Enqueue("检查订单状态"); taskQueue.Enqueue("更新数据库");
Console.WriteLine($"任务数量: {taskQueue.Count}");
// 提取任务 (Dequeue) — 删除并返回第一个元素 string firstTask = taskQueue.Dequeue(); Console.WriteLine($"正在执行: {firstTask}");
// 查看下一个任务但不删除 (Peek) string nextTask = taskQueue.Peek(); Console.WriteLine($"下一个: {nextTask}");
Console.WriteLine($"剩余任务: {taskQueue.Count}"); }}程序输出:
任务数量: 3正在执行: 发送邮件给客户下一个: 检查订单状态剩余任务: 2Queue<T> 的重要方法
Enqueue(item)— 将元素添加到队列末尾。Dequeue()— 删除并返回第一个元素。如果队列为空,则抛出InvalidOperationException异常。Peek()— 返回第一个元素但不删除。Clear()— 清空队列。Contains(item)— 检查队列中是否存在某个元素。
重要提示: Queue<T> 不是线程安全的。如果多个线程同时添加或提取元素,将会出现错误。对于多线程场景,请使用 ConcurrentQueue<T>。
2. 线程安全队列:ConcurrentQueue<T>
在实际应用程序中,任务通常在不同的线程中添加和处理。为此,.NET 提供了 ConcurrentQueue<T>,它位于 System.Collections.Concurrent 命名空间中。
示例:生产者-消费者模式
想象一下,一个线程(生产者)生成任务,另一个线程(消费者)执行它们。
using System;using System.Collections.Concurrent;using System.Threading.Tasks;
class Program{ static void Main() { ConcurrentQueue<string> queue = new ConcurrentQueue<string>();
// 生产者:添加任务