一问题
假设:
你在做上位机。
有两个线程:
线程1(PLC采集线程)
不停读取PLC:
每10ms读取一次数据
线程2(数据库线程)
负责保存数据:
保存一次需要100ms
问题来了
PLC数据来的速度:
远远快于数据库保存速度
怎么办?
二解决办法:队列缓冲
先存起来,搞一个队列出来,PLC线程只负责采集数据,然后放入队列,数据库线程,慢慢从队列中取数据,保存到数据库
这就是生产者消费者模型
三普通队列Queue
3.1.场景
平常存数据用,集合,数组,数据库都是随便查,随便删,随机访问。
而队列不支持随机访问,只能先进先出
场景1:消息排队、
- 聊天消息接收
- 订单处理
- 日志写入
- 后台任务排队
- 游戏技能释放队列
场景 2:广度优先搜索 BFS(算法)
比如迷宫寻路、层级遍历,必须用队列。
场景 3:异步任务排队
比如:
- 上传文件队列
- 打印队列
- 接口请求限流队列
这些都是必须排队的业务。
3.2特点
- 只能从尾部加数据
- 只能从头部取数据
- 不能随机访问(不能取第 3 个、第 5 个)
- 按顺序处理
- 内部自动扩容
- 配合 BFS(广度优先)必用
3.3核心方法
| 方法 | 作用 |
|---|---|
| Enqueue(item) | 把数据加到队尾 |
| Dequeue() | 取出并删除队首数据 |
| Peek() | 查看队首,但不删除 |
| Clear() | 清空队列 |
| Contains(item) | 判断是否包含某个数据 |
额外常用:
Count:队列里有多少个数据
3.4实例
cs
// 1. 创建队列
Queue<string> queue = new Queue<string>();
// 2. 入队(加到尾部)
queue.Enqueue("第1个人");
queue.Enqueue("第2个人");
queue.Enqueue("第3个人");
// 现在队列:第1 → 第2 → 第3
// 3. 查看队首(不删除)
string first = queue.Peek();
Console.WriteLine(first); // 输出:第1个人
// 4. 出队(取出并删除队首)
string a = queue.Dequeue(); // 取出:第1个人
string b = queue.Dequeue(); // 取出:第2个人
// 现在队列只剩:第3个人
// 5. 遍历队列(不会删除数据)
foreach(var item in queue)
{
Console.WriteLine(item);
}
四多线程安全队列ConcurrentQuenue
4.1用途
ConcurrentQueue = 线程安全的队列 专门给多线程、并发场景用的。
如果:
- 线程 A:往队列加数据
- 线程 B:从队列取数据
普通 Queue 会直接崩溃、数据丢失、卡死
ConcurrentQueue 就是为了解决这个问题诞生的!
4.2特点
- 线程安全
- 先进先出
- 无锁/轻量级锁
- 不能修改中间元素
- 不能随机访问
- 配合多线程BFS也能用
4.3核心方法
| 方法 | 作用 |
|---|---|
| Enqueue(item) | 加到队尾(和普通 Queue 一样) |
| TryDequeue(out item) | 尝试取出队首,并删除(安全取出) |
| TryPeek(out item) | 尝试查看队首,不删除 |
| IsEmpty | 判断队列是否为空 |
| Count | 元素数量(不推荐频繁用) |
**为什么带 Try?**多线程时,可能刚好被别的线程取走了,所以要 "尝试取"。
4.4实例
cs
// 1. 创建并发队列
ConcurrentQueue<int> queue = new ConcurrentQueue<int>();
// 2. 入队(和普通一样)
queue.Enqueue(10);
queue.Enqueue(20);
queue.Enqueue(30);
// 3. Try 取出队首(安全!)
if (queue.TryDequeue(out int result))
{
Console.WriteLine("取出:" + result); // 10
}
// 4. Try 查看队首(不删除)
if (queue.TryPeek(out int first))
{
Console.WriteLine("队首是:" + first); // 20
}
// 5. 判断空
bool empty = queue.IsEmpty;
4.5场景实例
cs
// 并发队列
ConcurrentQueue<string> taskQueue = new ConcurrentQueue<string>();
// 生产者线程:往队列加任务
Task.Run(() =>
{
for (int i = 1; i <= 5; i++)
{
taskQueue.Enqueue("任务" + i);
Console.WriteLine("生产:任务" + i);
Task.Delay(300).Wait();
}
});
// 消费者线程:从队列取任务
Task.Run(() =>
{
while (true)
{
if (taskQueue.TryDequeue(out string task))
{
Console.WriteLine("处理:" + task);
}
Task.Delay(200).Wait();
}
});
Console.ReadLine();