ConcurrentQueue并发队列和SemaphoreSlim轻量级信号量
ConcurrentQueue 并发队列
SemaphoreSlim 轻量级信号量
可以理解为:“限制多少个人同时进房间”
这两个是做 生产者 - 消费者、高频数据采集、多线程采集不卡顿 的核心工具,也是工控上位机必用知识点。
一、ConcurrentQueue 并发队列
1. 是什么?
ConcurrentQueue<T> = 线程安全的先进先出队列普通
工控场景:
Queue<T> 不是线程安全的,多线程同时读写会直接报错或数据错乱。
- 采集线程(生产者)不断入队
- 处理线程(消费者)不断出队
必须用 ConcurrentQueue
2. 核心特点
- 线程安全,无需 lock
- 无边界(默认无限长度)
- 高性能,适合高并发
- 先进先出 FIFO
3. 常用方法
1 // 入队(生产者) 2 queue.Enqueue(item); 3 4 // 尝试出队(消费者) 5 if (queue.TryDequeue(out var item)) 6 { 7 // 处理数据 8 } 9 10 // 查看队首元素,不出队 11 if (queue.TryPeek(out var item)) 12 13 // 清空队列 14 while (queue.TryDequeue(out _)) { } 15 16 // 获取数量 17 int count = queue.Count;
4. 工业上位机典型用法
1 // 全局并发队列 2 private readonly ConcurrentQueue<DeviceData> _dataQueue = new(); 3 4 // 生产者:PLC 采集线程 5 void CollectThread() 6 { 7 while (true) 8 { 9 var data = ReadFromPLC(); // 从 PLC 读取 10 _dataQueue.Enqueue(data); // 入队 11 Thread.Sleep(100); 12 } 13 } 14 15 // 消费者:数据处理线程 16 void ProcessThread() 17 { 18 while (true) 19 { 20 if (_dataQueue.TryDequeue(out var data)) 21 { 22 // 更新UI、写库、报警判断 23 } 24 Thread.Sleep(10); 25 } 26 }
优点:
- 采集不卡处理
- 处理不拖慢采集
- 不会丢数据
- 不会报集合修改异常
二、SemaphoreSlim 轻量级信号量
1. 是什么?
SemaphoreSlim = 控制同时运行的线程数量可以理解为:“限制多少个人同时进房间”
工控上位机用途:
- 限制同时处理的任务数
- 实现 等待 + 通知 机制
- 解决消费者线程空转浪费 CPU
- 生产者生产后通知消费者 “来活了”
2. 核心作用
- 限流(最多 N 个线程同时运行)
- 线程同步(生产者通知消费者)
- 避免消费者无限循环耗 CPU
3. 常用方法
1 // 创建信号量,初始0,最大1000 2 var semaphore = new SemaphoreSlim(0, 1000); 3 4 // 等待信号(阻塞直到有信号) 5 semaphore.Wait(); 6 7 // 异步等待(不阻塞线程,上位机常用) 8 await semaphore.WaitAsync(); 9 10 // 释放一个信号 11 semaphore.Release(); 12 13 // 释放多个信号 14 semaphore.Release(5);
4. 为什么上位机必须用它?
如果只用
ConcurrentQueue,消费者要这样写:1 while (true) 2 { 3 if (queue.TryDequeue(...)) { ... } 4 Thread.Sleep(10); 5 }
问题:
- CPU 一直空转
- 延迟高
- 浪费资源
使用 SemaphoreSlim 后:
- 没数据时 线程休眠,不占 CPU
- 有数据时 立即唤醒
- 工业标准用法
三、终极组合:ConcurrentQueue + SemaphoreSlim
(工控上位机标准生产者 - 消费者架构)
完整示例代码
1 // 数据实体 2 public class DeviceData 3 { 4 public float Temperature { get; set; } 5 public DateTime Time { get; set; } 6 } 7 8 public class DataProcessService 9 { 10 // 并发队列 11 private readonly ConcurrentQueue<DeviceData> _queue = new(); 12 13 // 信号量 14 private readonly SemaphoreSlim _semaphore = new(0, int.MaxValue); 15 16 // 运行标志 17 private bool _isRunning; 18 19 // 启动消费者 20 public void StartConsumer() 21 { 22 _isRunning = true; 23 24 Task.Run(async () => 25 { 26 while (_isRunning) 27 { 28 try 29 { 30 // 等待信号(没数据就休眠) 31 await _semaphore.WaitAsync(); 32 33 // 取出数据 34 if (_queue.TryDequeue(out var data)) 35 { 36 // 处理:更新UI、写库、报警 37 ProcessData(data); 38 } 39 } 40 catch (Exception ex) 41 { 42 // 异常处理 43 } 44 } 45 }); 46 } 47 48 // 生产者:往队列里扔数据 49 public void AddData(DeviceData data) 50 { 51 _queue.Enqueue(data); 52 _semaphore.Release(); // 通知消费者来取 53 } 54 55 // 模拟数据处理 56 private void ProcessData(DeviceData data) 57 { 58 Console.WriteLine($"处理温度:{data.Temperature} ℃"); 59 } 60 61 public void Stop() 62 { 63 _isRunning = false; 64 } 65 }
使用方式
1 var service = new DataProcessService(); 2 service.StartConsumer(); 3 4 // 模拟 PLC 采集线程(生产者) 5 while (true) 6 { 7 var data = new DeviceData 8 { 9 Temperature = 50 + new Random().Next(0, 20), 10 Time = DateTime.Now 11 }; 12 13 service.AddData(data); 14 Thread.Sleep(100); 15 }
四、两者配合的真正优势(工控必看)
1. ConcurrentQueue 负责
- 线程安全存取数据
- 保证顺序
- 不丢包
2. SemaphoreSlim 负责
- 没有数据时 不占用 CPU
- 有数据时 立即唤醒处理
- 避免死循环空转
- 实现高效的生产消费模型
3. 最终效果
- 采集 1ms 也不卡
- 处理再慢也不影响采集
- CPU 占用极低
- 上位机界面永远流畅
- 工业标准、稳定可靠
五、一句话总结
- ConcurrentQueue = 线程安全队列,负责存数据
- SemaphoreSlim = 线程通知器,负责唤醒消费者
- 两者组合 = 工业上位机标准生产者 - 消费者模型
浙公网安备 33010602011771号