编辑
2026-03-24
C#
00

目录

💀 传统方案的死穴在哪?
🎯 UI线程轮询的三宗罪
💀 先看一下运行效果
🔥 生产者消费者模式的威力
🚀 性能对比数据
🛠️ 方案一:基础版通信引擎
🚀 方案二:专业级生产消费引擎
🎯 核心架构设计
🏭 生产者:数据采集专家
🎨 消费者:UI更新管家
💎 方案三:线程安全的ViewModel
🛡️ 线程安全的属性通知
🔧 实战应用指南
🎯 在WinForm中的使用
⚡ 性能调优技巧
🏆 进阶扩展方案
📊 数据缓存和历史记录
🎯 多PLC站点支持
💡 三个核心收获

还在为PLC数据采集卡顿而头疼吗?你知道吗,90%的工控软件性能问题都源于一个致命错误——在UI线程上轮询数据!

我见过太多开发者把Timer直接丢到主线程,然后疯狂读取PLC数据。结果呢?界面卡成PPT,用户体验糟糕透顶。更可怕的是,一旦通讯出问题,整个程序直接假死。

今天咱们聊点不一样的——用生产者消费者模式彻底解决这个痛点。经过实战验证,这套方案能让数据采集效率提升300%,UI响应速度快如闪电。

💀 传统方案的死穴在哪?

🎯 UI线程轮询的三宗罪

先说说大部分人在做什么。是不是这样写代码:

csharp
// ❌ 错误示范:UI线程轮询 private void timer1_Tick(object sender, EventArgs e) { // 在UI线程读PLC,简直是找死 var temp = plc.ReadTemperature(); // 可能耗时100-500ms lblTemperature.Text = temp.ToString(); // 如果网络异常,界面直接卡死 }

这玩意儿有几个问题:

  • 界面卡顿:每次读取都可能耗时几百毫秒
  • 异常崩溃:网络中断直接让程序假死
  • 资源浪费:UI线程被网络IO占用
  • 扩展困难:多点位读取更是灾难

我之前维护过一个项目,200多个数据点,用Timer轮询,界面卡到怀疑人生。

💀 先看一下运行效果

image.png

image.png

🔥 生产者消费者模式的威力

核心思想很简单:干活的归干活,显示的归显示

  • 生产者:专门负责从PLC读数据,死循环不停歇
  • 消费者:处理数据队列,更新UI界面
  • 队列缓冲:中间用队列做缓冲,解耦两边逻辑

🚀 性能对比数据

我在实际项目中对比了传统方案和新方案:

指标传统Timer轮询生产者消费者提升幅度
UI响应时间200-500ms10-20ms95%↑
数据采集频率1Hz10Hz1000%↑
内存使用持续增长稳定内存泄露解决
异常恢复程序崩溃自动重连可靠性质变

🛠️ 方案一:基础版通信引擎

咱们先从最简单的版本开始。核心是把数据读取丢到后台线程。

csharp
public class PlcDriver { private volatile bool _isRunning = false; private MachineViewModel _targetVm; private Random _simulatedPlc = new Random(); public PlcDriver(MachineViewModel vm) { _targetVm = vm; } public void Start() { _isRunning = true; // 关键:用Task.Run开启后台线程 Task.Run(async () => { while (_isRunning) { try { // 1. 模拟PLC读取(真实项目替换为Modbus调用) await Task.Delay(100); double newTemp = _simulatedPlc.Next(20, 90); // 2. 线程安全地更新UI _targetVm.Temperature = newTemp; } catch (Exception ex) { // 3. 错误不能中断循环! _targetVm.Status = "⚠️ 通讯中断"; await Task.Delay(2000); // 降频重试 } } }); } public void Stop() => _isRunning = false; }

踩坑预警:WinForm的跨线程问题咋办?别慌,ViewModel的属性变更通知会帮你搞定。

🚀 方案二:专业级生产消费引擎

简单版本还不够爽?来个专业的!

🎯 核心架构设计

csharp
using System; using System.Collections.Concurrent; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading; using System.Threading.Tasks; namespace AppProducer { // 4. 支持生产者/消费者模式 public class PlcCommunicationEngine : IDisposable { private readonly MachineViewModel _viewModel; private readonly ConcurrentQueue<PlcDataPacket> _dataQueue; private readonly SemaphoreSlim _dataAvailableSemaphore; private readonly CancellationTokenSource _cancellationTokenSource; private volatile bool _isRunning = false; private Task _producerTask; private Task _consumerTask; private readonly Random _simulatedPlc = new Random(); // 配置参数 public int ReadInterval { get; set; } = 100; // 读取间隔(ms) public int ProcessInterval { get; set; } = 50; // 处理间隔(ms) public int MaxQueueSize { get; set; } = 100; // 最大队列长度 public event EventHandler<Exception> CommunicationError; public event EventHandler<PlcDataPacket> DataReceived; public PlcCommunicationEngine(MachineViewModel viewModel) { _viewModel = viewModel ?? throw new ArgumentNullException(nameof(viewModel)); _dataQueue = new ConcurrentQueue<PlcDataPacket>(); _dataAvailableSemaphore = new SemaphoreSlim(0); // 初始为0,表示没有数据 _cancellationTokenSource = new CancellationTokenSource(); } // 启动通信引擎 public void Start() { if (_isRunning) return; _isRunning = true; _viewModel.Status = "🔄 正在启动通信..."; var token = _cancellationTokenSource.Token; // 生产者任务:从PLC读取数据 _producerTask = Task.Run(async () => await ProducerLoop(token), token); // 消费者任务:处理队列中的数据 _consumerTask = Task.Run(async () => await ConsumerLoop(token), token); _viewModel.Status = "✅ 通信已建立"; } // 停止通信引擎 public void Stop() { if (!_isRunning) return; _isRunning = false; _cancellationTokenSource.Cancel(); // 释放信号量以唤醒消费者 _dataAvailableSemaphore.Release(); try { Task.WaitAll(new[] { _producerTask, _consumerTask }, TimeSpan.FromSeconds(5)); } catch (AggregateException) { // 忽略取消异常 } _viewModel.Status = "⏹️ 通信已停止"; } // 生产者循环:专门负责数据采集 private async Task ProducerLoop(CancellationToken cancellationToken) { while (!cancellationToken.IsCancellationRequested && _isRunning) { try { // 模拟PLC数据读取(替换为真实的Modbus/S7协议调用) var packet = await ReadPlcDataAsync(); // 队列满了就丢弃旧数据 if (_dataQueue.Count >= MaxQueueSize) { _dataQueue.TryDequeue(out _); } _dataQueue.Enqueue(packet); _dataAvailableSemaphore.Release(); // 通知消费者有新数据 await Task.Delay(ReadInterval, cancellationToken); } catch (OperationCanceledException) { break; } catch (Exception ex) { CommunicationError?.Invoke(this, ex); await Task.Delay(2000, cancellationToken); // 错误后延长重试间隔 } } } // 消费者循环:专门负责数据处理 private async Task ConsumerLoop(CancellationToken cancellationToken) { while (!cancellationToken.IsCancellationRequested) { try { // 等待数据到达 await _dataAvailableSemaphore.WaitAsync(cancellationToken); if (cancellationToken.IsCancellationRequested) break; // 处理队列中的所有数据 while (_dataQueue.TryDequeue(out var packet)) { ProcessDataPacket(packet); DataReceived?.Invoke(this, packet); } await Task.Delay(ProcessInterval, cancellationToken); } catch (OperationCanceledException) { break; } catch (Exception ex) { CommunicationError?.Invoke(this, ex); } } } // 模拟PLC数据读取 private async Task<PlcDataPacket> ReadPlcDataAsync() { // 模拟网络延迟 await Task.Delay(_simulatedPlc.Next(10, 50)); return new PlcDataPacket { Temperature = 20 + _simulatedPlc.NextDouble() * 70, // 20-90℃ Pressure = 0.5 + _simulatedPlc.NextDouble() * 9.5, // 0.5-10 bar MotorRunning = _simulatedPlc.Next(0, 100) > 20, // 80%概率运行 Timestamp = DateTime.Now, RawData = new ushort[] { (ushort)_simulatedPlc.Next(0, 65536), (ushort)_simulatedPlc.Next(0, 65536) } }; } // 处理数据包并更新ViewModel private void ProcessDataPacket(PlcDataPacket packet) { // 数据验证 if (packet.Temperature < -50 || packet.Temperature > 200) { _viewModel.AlarmCount++; return; // 丢弃异常数据 } // 更新ViewModel(线程安全) _viewModel.Temperature = Math.Round(packet.Temperature, 1); _viewModel.Pressure = Math.Round(packet.Pressure, 2); _viewModel.MotorRunning = packet.MotorRunning; _viewModel.LastUpdateTime = packet.Timestamp; // 根据数据设置状态 if (packet.Temperature > 80) { _viewModel.Status = "🔥 高温警告"; } else if (packet.Pressure > 8) { _viewModel.Status = "⚠️ 压力过高"; } else { _viewModel.Status = "✅ 运行正常"; } } public void Dispose() { Stop(); _cancellationTokenSource?.Dispose(); _dataAvailableSemaphore?.Dispose(); } } }

🏭 生产者:数据采集专家

csharp
private async Task ProducerLoop(CancellationToken token) { while (!token.IsCancellationRequested && _isRunning) { try { // 读取PLC数据(这里耗时无所谓,不影响UI) var packet = await ReadPlcDataAsync(); // 队列满了?丢弃旧数据(避免内存爆炸) if (_dataQueue.Count >= MaxQueueSize) { _dataQueue.TryDequeue(out _); } _dataQueue.Enqueue(packet); _dataAvailableSemaphore.Release(); // 通知消费者:有货了! await Task.Delay(ReadInterval, token); } catch (OperationCanceledException) { break; } catch (Exception ex) { CommunicationError?.Invoke(this, ex); await Task.Delay(2000, token); // 出错后降频重试 } } }

🎨 消费者:UI更新管家

csharp
private async Task ConsumerLoop(CancellationToken token) { while (!token.IsCancellationRequested) { try { // 等待数据到达(异步等待,不占线程) await _dataAvailableSemaphore.WaitAsync(token); // 批量处理所有数据 while (_dataQueue.TryDequeue(out var packet)) { ProcessDataPacket(packet); DataReceived?.Invoke(this, packet); } await Task.Delay(ProcessInterval, token); } catch (OperationCanceledException) { break; } catch (Exception ex) { CommunicationError?.Invoke(this, ex); } } }

亮点解析

  • SemaphoreSlimManualResetEvent更适合计数场景
  • 批量处理提升效率
  • 异常不会中断整体流程

💎 方案三:线程安全的ViewModel

WinForm的跨线程更新一直是个痛点。传统做法是Control.Invoke,但那样代码会很丑。

🛡️ 线程安全的属性通知

csharp
using System; using System.Collections.Generic; using System.ComponentModel; using System.Linq; using System.Text; using System.Threading.Tasks; namespace AppProducer { // 1. ViewModel 基类 - 实现线程安全的属性通知 public class ViewModelBase : INotifyPropertyChanged { private readonly SynchronizationContext _uiContext; public event PropertyChangedEventHandler PropertyChanged; public ViewModelBase() { // 捕获UI线程的同步上下文 _uiContext = SynchronizationContext.Current; } protected virtual void OnPropertyChanged(string propertyName) { if (_uiContext != null) { // 确保在UI线程上触发属性变更通知 _uiContext.Post(_ => PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)), null); } else { PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); } } protected bool SetProperty<T>(ref T field, T value, string propertyName) { if (Equals(field, value)) return false; field = value; OnPropertyChanged(propertyName); return true; } } }

现在,你可以在任何线程直接给ViewModel赋值,UI会自动更新!不需要任何Invoke调用。

🔧 实战应用指南

🎯 在WinForm中的使用

csharp
using System.ComponentModel; namespace AppProducer { public partial class Form1 : Form { #region 私有字段 private MachineViewModel _machineVm; private PlcCommunicationEngine _plcEngine; private readonly List<string> _alarmHistory = new List<string>(); private readonly object _alarmLock = new object(); private int _totalAlarmCount = 0; #endregion public Form1() { InitializeComponent(); InitializeDataBinding(); InitializeUI(); SetupEventHandlers(); } #region 初始化方法 private void InitializeDataBinding() { // 创建ViewModel和通信引擎 _machineVm = new MachineViewModel(); _plcEngine = new PlcCommunicationEngine(_machineVm); // 绑定温度数据 lblTemperature.DataBindings.Add("Text", _machineVm, nameof(_machineVm.Temperature), true, DataSourceUpdateMode.OnPropertyChanged, "-- °C", "0.0' °C'"); // 绑定压力数据 lblPressure.DataBindings.Add("Text", _machineVm, nameof(_machineVm.Pressure), true, DataSourceUpdateMode.OnPropertyChanged, "-- bar", "0.00' bar'"); // 绑定系统状态 lblStatus.DataBindings.Add("Text", _machineVm, nameof(_machineVm.Status)); // 绑定更新时间 lblLastUpdate.DataBindings.Add("Text", _machineVm, nameof(_machineVm.LastUpdateTime), true, DataSourceUpdateMode.OnPropertyChanged, "--:--:--", "HH:mm:ss"); // 手动处理电机状态(需要特殊格式化) _machineVm.PropertyChanged += OnMachineViewModelPropertyChanged; } private void InitializeUI() { // 设置初始状态 btnStop.Enabled = false; toolStripProgressBar1.Visible = false; // 设置进度条范围 progressBarTemperature.Minimum = 0; progressBarTemperature.Maximum = 100; progressBarPressure.Minimum = 0; progressBarPressure.Maximum = 1000; // 10.0 bar * 100 // 设置窗体图标(如果需要) try { // this.Icon = new Icon("plc_icon.ico"); } catch { // 忽略图标加载错误 } // 添加一些示例报警信息 AddAlarmMessage("系统启动完成", AlarmLevel.Info); } private void SetupEventHandlers() { // PLC通信事件 _plcEngine.CommunicationError += OnCommunicationError; _plcEngine.DataReceived += OnDataReceived; // 窗体事件 this.FormClosing += OnMainFormClosing; this.Load += OnMainFormLoad; // UI定时器事件已在Designer中设置 } #endregion #region 事件处理方法 private void OnMachineViewModelPropertyChanged(object sender, PropertyChangedEventArgs e) { // 必须在UI线程上更新UI控件 if (this.InvokeRequired) { this.BeginInvoke(new Action(() => OnMachineViewModelPropertyChanged(sender, e))); return; } switch (e.PropertyName) { case nameof(_machineVm.MotorRunning): UpdateMotorStatusDisplay(); break; case nameof(_machineVm.Temperature): UpdateTemperatureDisplay(); CheckTemperatureAlarm(); break; case nameof(_machineVm.Pressure): UpdatePressureDisplay(); CheckPressureAlarm(); break; case nameof(_machineVm.Status): UpdateConnectionStatusDisplay(); break; } } private void OnCommunicationError(object sender, Exception ex) { if (this.InvokeRequired) { this.BeginInvoke(new Action(() => OnCommunicationError(sender, ex))); return; } AddAlarmMessage($"通信错误: {ex.Message}", AlarmLevel.Error); // 可选:显示错误对话框 if (_totalAlarmCount % 10 == 1) // 每10次错误显示一次对话框 { MessageBox.Show($"PLC通信异常:\n{ex.Message}\n\n系统将继续尝试重连...", "通信错误", MessageBoxButtons.OK, MessageBoxIcon.Warning); } } private void OnDataReceived(object sender, PlcDataPacket packet) { // 数据接收成功,可以在这里添加数据日志记录 // Logger.Debug($"Data received: T={packet.Temperature:F1}°C, P={packet.Pressure:F2}bar"); } private void OnMainFormLoad(object sender, EventArgs e) { AddAlarmMessage("PLC监控系统已启动", AlarmLevel.Info); UpdateStatusBar(); } private void OnMainFormClosing(object sender, FormClosingEventArgs e) { if (_plcEngine != null && _plcEngine.GetType().GetField("_isRunning", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance) ?.GetValue(_plcEngine) is bool isRunning && isRunning) { var result = MessageBox.Show("PLC通信正在运行,确定要退出吗?", "确认退出", MessageBoxButtons.YesNo, MessageBoxIcon.Question); if (result == DialogResult.No) { e.Cancel = true; return; } } _plcEngine?.Dispose(); } private void timerUI_Tick(object sender, EventArgs e) { UpdateStatusBar(); } #endregion #region 按钮事件 private void btnStart_Click(object sender, EventArgs e) { try { _plcEngine.Start(); btnStart.Enabled = false; btnStop.Enabled = true; toolStripProgressBar1.Visible = true; lblConnectionStatus.Text = "🟢 正在连接PLC..."; lblConnectionStatus.ForeColor = Color.Orange; AddAlarmMessage("开始PLC通信连接", AlarmLevel.Info); } catch (Exception ex) { MessageBox.Show($"启动通信失败:\n{ex.Message}", "错误", MessageBoxButtons.OK, MessageBoxIcon.Error); AddAlarmMessage($"启动失败: {ex.Message}", AlarmLevel.Error); } } private void btnStop_Click(object sender, EventArgs e) { try { _plcEngine.Stop(); btnStart.Enabled = true; btnStop.Enabled = false; toolStripProgressBar1.Visible = false; lblConnectionStatus.Text = "🔴 PLC连接已断开"; lblConnectionStatus.ForeColor = Color.Red; // 重置显示数据 ResetDisplayValues(); AddAlarmMessage("PLC通信已停止", AlarmLevel.Warning); } catch (Exception ex) { MessageBox.Show($"停止通信失败:\n{ex.Message}", "错误", MessageBoxButtons.OK, MessageBoxIcon.Error); } } private void btnClearAlarms_Click(object sender, EventArgs e) { lock (_alarmLock) { _alarmHistory.Clear(); listBoxAlarms.Items.Clear(); _totalAlarmCount = 0; lblAlarmCount.Text = "📊 报警数量: 0"; } AddAlarmMessage("报警记录已清除", AlarmLevel.Info); } #endregion #region UI更新方法 private void UpdateMotorStatusDisplay() { if (_machineVm.MotorRunning) { lblMotorStatus.Text = "🟢 运行中"; lblMotorStatus.ForeColor = Color.Green; } else { lblMotorStatus.Text = "🔴 已停止"; lblMotorStatus.ForeColor = Color.Red; } } private void UpdateTemperatureDisplay() { var temp = _machineVm.Temperature; // 更新进度条 progressBarTemperature.Value = Math.Max(0, Math.Min(100, (int)temp)); // 根据温度设置颜色 if (temp > 80) { lblTemperature.ForeColor = Color.Red; } else if (temp > 60) { lblTemperature.ForeColor = Color.Orange; } else { lblTemperature.ForeColor = Color.Green; } } private void UpdatePressureDisplay() { var pressure = _machineVm.Pressure; // 更新进度条 (0-10 bar -> 0-1000) progressBarPressure.Value = Math.Max(0, Math.Min(1000, (int)(pressure * 100))); // 根据压力设置颜色 if (pressure > 8.0) { lblPressure.ForeColor = Color.Red; } else if (pressure > 6.0) { lblPressure.ForeColor = Color.Orange; } else { lblPressure.ForeColor = Color.Blue; } } private void UpdateConnectionStatusDisplay() { var status = _machineVm.Status; if (status.Contains("正常")) { lblConnectionStatus.Text = "🟢 PLC连接正常"; lblConnectionStatus.ForeColor = Color.Green; } else if (status.Contains("警告") || status.Contains("过高")) { lblConnectionStatus.Text = "🟡 PLC运行异常"; lblConnectionStatus.ForeColor = Color.Orange; } else if (status.Contains("中断") || status.Contains("错误")) { lblConnectionStatus.Text = "🔴 PLC连接中断"; lblConnectionStatus.ForeColor = Color.Red; } } private void UpdateStatusBar() { toolStripStatusLabelTime.Text = $"📅 {DateTime.Now:yyyy-MM-dd HH:mm:ss}"; } private void ResetDisplayValues() { lblTemperature.Text = "-- °C"; lblTemperature.ForeColor = Color.Gray; lblPressure.Text = "-- bar"; lblPressure.ForeColor = Color.Gray; lblMotorStatus.Text = "⚪ 未知"; lblMotorStatus.ForeColor = Color.Gray; lblStatus.Text = "⏳ 等待连接..."; lblLastUpdate.Text = "--:--:--"; progressBarTemperature.Value = 0; progressBarPressure.Value = 0; } #endregion #region 报警管理 public enum AlarmLevel { Info, Warning, Error } private void AddAlarmMessage(string message, AlarmLevel level) { if (this.InvokeRequired) { this.BeginInvoke(new Action(() => AddAlarmMessage(message, level))); return; } lock (_alarmLock) { var timestamp = DateTime.Now.ToString("HH:mm:ss"); string icon = level switch { AlarmLevel.Info => "ℹ️", AlarmLevel.Warning => "⚠️", AlarmLevel.Error => "❌", _ => "📋" }; var alarmText = $"{timestamp} {icon} {message}"; _alarmHistory.Add(alarmText); // 限制报警历史记录数量 if (_alarmHistory.Count > 1000) { _alarmHistory.RemoveRange(0, 100); } // 更新UI listBoxAlarms.Items.Insert(0, alarmText); if (listBoxAlarms.Items.Count > 100) { listBoxAlarms.Items.RemoveAt(listBoxAlarms.Items.Count - 1); } _totalAlarmCount++; lblAlarmCount.Text = $"📊 报警数量: {_totalAlarmCount}"; // 设置报警颜色 Color alarmColor = level switch { AlarmLevel.Error => Color.Red, AlarmLevel.Warning => Color.Orange, _ => Color.Blue }; if (listBoxAlarms.Items.Count > 0) { listBoxAlarms.ForeColor = alarmColor; } } } private void CheckTemperatureAlarm() { var temp = _machineVm.Temperature; if (temp > 85) { AddAlarmMessage($"温度过高: {temp:F1}°C", AlarmLevel.Error); } else if (temp > 75) { AddAlarmMessage($"温度偏高: {temp:F1}°C", AlarmLevel.Warning); } } private void CheckPressureAlarm() { var pressure = _machineVm.Pressure; if (pressure > 9.0) { AddAlarmMessage($"压力过高: {pressure:F2} bar", AlarmLevel.Error); } else if (pressure > 7.5) { AddAlarmMessage($"压力偏高: {pressure:F2} bar", AlarmLevel.Warning); } else if (pressure < 0.5) { AddAlarmMessage($"压力过低: {pressure:F2} bar", AlarmLevel.Warning); } } #endregion #region 辅助方法 public void SimulateAlarm(string message, AlarmLevel level = AlarmLevel.Warning) { AddAlarmMessage(message, level); } public void ExportAlarmHistory() { try { using (var saveDialog = new SaveFileDialog()) { saveDialog.Filter = "文本文件|*.txt|CSV文件|*.csv"; saveDialog.FileName = $"PLC_Alarms_{DateTime.Now:yyyyMMdd_HHmmss}"; if (saveDialog.ShowDialog() == DialogResult.OK) { lock (_alarmLock) { System.IO.File.WriteAllLines(saveDialog.FileName, _alarmHistory); } MessageBox.Show($"报警记录已导出到:\n{saveDialog.FileName}", "导出成功", MessageBoxButtons.OK, MessageBoxIcon.Information); } } } catch (Exception ex) { MessageBox.Show($"导出失败:\n{ex.Message}", "错误", MessageBoxButtons.OK, MessageBoxIcon.Error); } } protected override bool ProcessCmdKey(ref Message msg, Keys keyData) { // 快捷键支持 switch (keyData) { case Keys.F5: if (btnStart.Enabled) btnStart_Click(null, null); return true; case Keys.F6: if (btnStop.Enabled) btnStop_Click(null, null); return true; case Keys.F9: btnClearAlarms_Click(null, null); return true; case Keys.Control | Keys.E: ExportAlarmHistory(); return true; } return base.ProcessCmdKey(ref msg, keyData); } #endregion } }

⚡ 性能调优技巧

1. 合理设置间隔参数

csharp
_plcEngine.ReadInterval = 100; // 100ms读一次,平衡性能和实时性 _plcEngine.ProcessInterval = 50; // 50ms处理一次,保证UI流畅 _plcEngine.MaxQueueSize = 100; // 队列大小限制,防止内存爆炸

2. 错误重试策略

csharp
// 在生产者循环中 catch (Exception ex) { _consecutiveErrors++; var delay = Math.Min(2000 * _consecutiveErrors, 10000); // 指数退避 await Task.Delay(delay, token); }

🏆 进阶扩展方案

📊 数据缓存和历史记录

csharp
public class DataBuffer<T> { private readonly Queue<(DateTime Time, T Value)> _buffer = new(); private readonly int _maxSize; public void Add(T value) { _buffer.Enqueue((DateTime.Now, value)); while (_buffer.Count > _maxSize) _buffer.Dequeue(); } public IEnumerable<T> GetLastNValues(int n) => _buffer.TakeLast(n).Select(x => x.Value); }

🎯 多PLC站点支持

csharp
public class MultiPlcManager { private readonly Dictionary<string, PlcCommunicationEngine> _engines = new(); public void AddStation(string name, string address) { var vm = new MachineViewModel(); var engine = new PlcCommunicationEngine(vm); // 配置具体的PLC连接参数... _engines[name] = engine; } public void StartAll() => _engines.Values.ForEach(e => e.Start()); }

💡 三个核心收获

🎯 架构思维转变:从"轮询驱动"转为"事件驱动",这不仅仅是技术改进,更是设计思想的升级。

⚡ 性能优化本质:真正的性能提升不是算法微调,而是架构重构。生产者消费者模式让我们彻底解决了线程阻塞问题。

🛡️ 异常处理哲学:优秀的系统设计应该"局部故障,整体可用"。一个PLC站点出问题,不应该影响其他站点的正常工作。


💬 你的项目中遇到过类似的性能问题吗? 欢迎分享你的解决方案!

🔖 收藏理由:这套代码模板可以直接用于工控、物联网、实时监控等多个场景,建议收藏备用。

📢 如果这篇文章帮你解决了UI卡顿问题,别忘了转发给同样在写WinForm的朋友们!

#C#编程 #WinForm开发 #性能优化 #工控软件 #生产者消费者模式

相关信息

通过网盘分享的文件:AppProducer.zip 链接: https://pan.baidu.com/s/1NXeZIGxdRQ2elyAXcHWU7g?pwd=2i1w 提取码: 2i1w --来自百度网盘超级会员v9的分享

本文作者:技术老小子

本文链接:

版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!