做C#上位机开发久了,你一定遇到过这些“老大难”问题:设备通信模块改一行代码,整个界面跟着卡死;数据采集和UI更新抢资源,日志里满是“跨线程操作无效”的异常;项目迭代3次后,代码变成“意大利面”,新增一个传感器要改十几个类;现场运行时突然崩溃,排查发现是配置管理类被重复创建了5次……
这些问题看似是“bug”,实则是架构设计的缺失。上位机软件要实现“7×24小时稳定运行、数据不丢包、界面不卡顿、后期易维护”,靠的不是“写代码时细心点”,而是用设计模式搭建底层骨架。本文聚焦上位机开发中最实用的5个设计模式——工厂模式、观察者模式、MVVM模式、单例模式、异步消息队列模式,结合工业设备通信、数据采集、UI交互等真实场景,用“问题+方案+代码”的方式拆解其应用,帮你从“能跑通”进阶到“写得稳”。
文章目录
一、先想清楚:上位机为什么需要设计模式?
上位机软件的核心诉求是**“稳定、可靠、可维护”**,而设计模式正是解决这些诉求的“通用模板”:
- 解耦:避免“牵一发而动全身”,比如设备通信模块和UI模块分离,改通信逻辑不影响界面;
- 复用:相同功能(如日志、配置)不用重复写代码,直接复用设计好的组件;
- 抗并发:处理多线程数据采集、UI更新时,避免资源竞争和死锁;
- 易维护:代码结构清晰,新人接手能快速理解,迭代新功能不用重构底层。
设计模式不是“炫技”,而是让上位机软件“经得起现场考验”的基础——那些运行几年不崩溃的工业上位机,背后一定藏着合理的设计模式应用。
二、实战:5个设计模式在上位机中的落地应用
1. 工厂模式:设备通信模块的“统一入口”
上位机痛点:项目中需要对接多种设备——串口传感器、网口PLC、USB工业相机,每种设备的通信逻辑不同(SerialPort、Socket、CameraSDK)。如果直接在代码里用if-else
判断设备类型,新增设备时要修改大量代码,还容易引入bug。
工厂模式核心:定义一个“工厂接口”,每种设备对应一个“具体工厂”,通过工厂创建设备通信实例,上层代码只需调用统一接口,不用关心具体实现。
代码实现:设备通信工厂
using System;
using System.IO.Ports;
// 1. 定义设备通信接口(统一方法)
public interface IDeviceCommunicator
{
bool Connect(); // 连接设备
bool Disconnect(); // 断开连接
byte[] ReadData(); // 读取数据
bool WriteData(byte[] data); // 写入数据
}
// 2. 具体设备实现(串口设备)
public class SerialCommunicator : IDeviceCommunicator
{
private SerialPort _serialPort;
private string _portName;
private int _baudRate;
// 构造函数:传入串口参数
public SerialCommunicator(string portName, int baudRate)
{
_portName = portName;
_baudRate = baudRate;
_serialPort = new SerialPort(portName, baudRate)
{
ReadTimeout = 1000,
WriteTimeout = 1000
};
}
public bool Connect()
{
try
{
if (!_serialPort.IsOpen)
_serialPort.Open();
return true;
}
catch (Exception ex)
{
Console.WriteLine($"串口连接失败:{ex.Message}");
return false;
}
}
public bool Disconnect()
{
if (_serialPort.IsOpen)
_serialPort.Close();
return true;
}
public byte[] ReadData()
{
try
{
int bytesToRead = _serialPort.BytesToRead;
if (bytesToRead == 0) return null;
byte[] data = new byte[bytesToRead];
_serialPort.Read(data, 0, bytesToRead);
return data;
}
catch (Exception ex)
{
Console.WriteLine($"串口读取失败:{ex.Message}");
return null;
}
}
public bool WriteData(byte[] data)
{
try
{
_serialPort.Write(data, 0, data.Length);
return true;
}
catch (Exception ex)
{
Console.WriteLine($"串口写入失败:{ex.Message}");
return false;
}
}
}
// 3. 具体设备实现(网口PLC)
public class TcpCommunicator : IDeviceCommunicator
{
private System.Net.Sockets.TCPClient _tcpClient;
private string _ip;
private int _port;
public TcpCommunicator(string ip, int port)
{
_ip = ip;
_port = port;
_tcpClient = new System.Net.Sockets.TCPClient();
}
public bool Connect()
{
try
{
_tcpClient.Connect(_ip, _port);
return true;
}
catch (Exception ex)
{
Console.WriteLine($"TCP连接失败:{ex.Message}");
return false;
}
}
// 其他方法(Disconnect、ReadData、WriteData)实现略...
}
// 4. 设备工厂(根据类型创建通信实例)
public static class DeviceCommunicatorFactory
{
public static IDeviceCommunicator CreateCommunicator(string deviceType, params object[] parameters)
{
return deviceType.ToLower() switch
{
"serial" => new SerialCommunicator((string)parameters[0], (int)parameters[1]),
"tcp" => new TcpCommunicator((string)parameters[0], (int)parameters[1]),
_ => throw new ArgumentException($"不支持的设备类型:{deviceType}")
};
}
}
// 5. 上层调用(不用关心具体设备类型)
public class DeviceManager
{
private IDeviceCommunicator _communicator;
public void InitDevice(string deviceType, params object[] parameters)
{
// 由工厂创建通信实例,上层只需调用接口方法
_communicator = DeviceCommunicatorFactory.CreateCommunicator(deviceType, parameters);
if (_communicator.Connect())
{
Console.WriteLine("设备连接成功!");
}
}
public void ReadDeviceData()
{
byte[] data = _communicator.ReadData();
if (data != null)
{
Console.WriteLine($"读取数据:{BitConverter.ToString(data)}");
}
}
}
优势:
- 新增设备(如USB相机)时,只需实现
IDeviceCommunicator
和对应的工厂分支,不用修改DeviceManager
等上层代码; - 通信逻辑集中在具体实现类,代码职责清晰,调试时能快速定位问题(比如串口问题只看
SerialCommunicator
)。
2. 观察者模式:数据采集与UI更新的“解耦神器”
上位机痛点:数据采集线程读取传感器数据后,需要更新UI界面(仪表盘、数据表格)、触发告警模块、保存日志文件。如果采集线程直接调用UI、告警、日志的方法,会导致“采集模块”和“消费模块”强耦合,新增一个消费模块(如数据上传云平台)要改采集代码。
观察者模式核心:定义“被观察者”(数据采集器)和“观察者”(UI、告警、日志),被观察者数据变化时,自动通知所有观察者,观察者根据数据做出响应,两者通过接口解耦。
代码实现:数据采集的观察者模式
using System;
using System.Collections.Generic;
// 1. 定义观察者接口(接收数据通知)
public interface IDataObserver
{
void OnDataReceived(byte[] data); // 数据接收通知
}
// 2. 定义被观察者接口(管理观察者、发送通知)
public interface IDataObservable
{
void AddObserver(IDataObserver observer); // 添加观察者
void RemoveObserver(IDataObserver observer); // 移除观察者
void NotifyObservers(byte[] data); // 通知所有观察者
}
// 3. 被观察者实现(数据采集器)
public class DataCollector : IDataObservable
{
private List<IDataObserver> _observers = new List<IDataObserver>();
private IDeviceCommunicator _communicator;
private System.Timers.Timer _collectTimer;
public DataCollector(IDeviceCommunicator communicator)
{
_communicator = communicator;
// 初始化采集定时器(100ms一次)
_collectTimer = new System.Timers.Timer(100)
{
AutoReset = true,
Enabled = false
};
_collectTimer.Elapsed += (s, e) => CollectData();
}
// 开始采集
public void StartCollect()
{
if (_communicator.Connect())
{
_collectTimer.Start();
}
}
// 停止采集
public void StopCollect()
{
_collectTimer.Stop();
_communicator.Disconnect();
}
// 采集数据并通知观察者
private void CollectData()
{
byte[] data = _communicator.ReadData();
if (data != null && data.Length > 0)
{
NotifyObservers(data); // 数据变化,通知所有观察者
}
}
// 实现被观察者接口方法
public void AddObserver(IDataObserver observer)
{
if (!_observers.Contains(observer))
{
_observers.Add(observer);
}
}
public void RemoveObserver(IDataObserver observer)
{
if (_observers.Contains(observer))
{
_observers.Remove(observer);
}
}
public void NotifyObservers(byte[] data)
{
foreach (var observer in _observers)
{
observer.OnDataReceived(data); // 调用每个观察者的通知方法
}
}
}
// 4. 观察者实现(UI更新模块)
public class UIDataObserver : IDataObserver
{
private MainForm _mainForm;
public UIDataObserver(MainForm mainForm)
{
_mainForm = mainForm;
}
public void OnDataReceived(byte[] data)
{
// 跨线程更新UI(假设数据是温湿度:前2字节温度,后2字节湿度)
int temp = BitConverter.ToInt16(data, 0);
int humi = BitConverter.ToInt16(data, 2);
_mainForm.UpdateSensorData(temp, humi);
}
}
// 5. 观察者实现(告警模块)
public class AlarmObserver : IDataObserver
{
private int _maxTemp = 50; // 温度阈值
public void OnDataReceived(byte[] data)
{
int temp = BitConverter.ToInt16(data, 0);
if (temp > _maxTemp)
{
Console.WriteLine($"告警:温度超过阈值!当前{temp}℃");
// 触发声光告警(代码略)
}
}
}
// 6. 上层调用(组装观察者和被观察者)
public class AppManager
{
public void InitApp()
{
// 1. 工厂创建设备通信实例
var communicator = DeviceCommunicatorFactory.CreateCommunicator("serial", "COM3", 9600);
// 2. 创建被观察者(数据采集器)
var dataCollector = new DataCollector(communicator);
// 3. 创建观察者
var uiObserver = new UIDataObserver(new MainForm());
var alarmObserver = new AlarmObserver();
// 4. 注册观察者
dataCollector.AddObserver(uiObserver);
dataCollector.AddObserver(alarmObserver);
// 5. 开始采集
dataCollector.StartCollect();
}
}
优势:
- 采集模块只负责“采集和通知”,不关心谁用数据;消费模块只负责“接收和处理”,不关心数据从哪来;
- 新增消费模块(如
CloudUploadObserver
上传云平台)时,只需实现IDataObserver
并注册,不用修改采集代码; - 避免采集线程直接操作UI,减少“跨线程异常”风险。
3. MVVM模式:UI与业务逻辑的“彻底分离”
上位机痛点:用WinForm开发时,习惯把业务逻辑(如数据解析、设备控制)写在Form类里,导致Form类动辄上千行代码。修改一个按钮逻辑可能影响数据解析,UI布局调整要动业务代码,后期维护极其困难。
MVVM模式核心:将软件分为Model(数据模型)、View(视图/UI)、ViewModel(视图模型) 三层——View只负责展示,ViewModel处理业务逻辑并暴露数据接口,Model定义数据结构,三者通过数据绑定通信,实现“UI与业务完全解耦”。
代码实现:WPF上位机的MVVM模式(WinForm也可借鉴)
using System.ComponentModel;
using System.Runtime.CompilerServices;
using System.Windows;
// 1. Model(数据模型:传感器数据)
public class SensorDataModel : INotifyPropertyChanged
{
private int _temperature;
private int _humidity;
// 温度属性(支持数据绑定)
public int Temperature
{
get => _temperature;
set
{
_temperature = value;
OnPropertyChanged(); // 属性变化时通知UI
}
}
// 湿度属性(支持数据绑定)
public int Humidity
{
get => _humidity;
set
{
_humidity = value;
OnPropertyChanged();
}
}
// 实现INotifyPropertyChanged接口(数据绑定必备)
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
// 2. ViewModel(视图模型:处理业务逻辑)
public class MainViewModel : INotifyPropertyChanged
{
private SensorDataModel _sensorData;
private DataCollector _dataCollector;
// 暴露给View的数据模型(用于绑定)
public SensorDataModel SensorData
{
get => _sensorData;
set
{
_sensorData = value;
OnPropertyChanged();
}
}
// 命令(View按钮绑定此命令,触发业务逻辑)
public RelayCommand StartCollectCommand { get; }
public RelayCommand StopCollectCommand { get; }
public MainViewModel()
{
// 初始化数据模型
SensorData = new SensorDataModel();
// 初始化数据采集器
var communicator = DeviceCommunicatorFactory.CreateCommunicator("serial", "COM3", 9600);
_dataCollector = new DataCollector(communicator);
// 注册观察者:更新数据模型
_dataCollector.AddObserver(new DataModelObserver(this));
// 初始化命令
StartCollectCommand = new RelayCommand(() => _dataCollector.StartCollect());
StopCollectCommand = new RelayCommand(() => _dataCollector.StopCollect());
}
// 数据模型观察者:接收采集数据并更新ViewModel
private class DataModelObserver : IDataObserver
{
private MainViewModel _viewModel;
public DataModelObserver(MainViewModel viewModel)
{
_viewModel = viewModel;
}
public void OnDataReceived(byte[] data)
{
// 解析数据并更新ViewModel的SensorData
_viewModel.SensorData.Temperature = BitConverter.ToInt16(data, 0);
_viewModel.SensorData.Humidity = BitConverter.ToInt16(data, 2);
}
}
// 实现INotifyPropertyChanged接口
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
// 3. View(视图:WPF界面,仅负责展示,无业务逻辑)
// MainWindow.xaml(UI代码):
/*
<Window x:Class="MvvmUpperMachine.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:vm="clr-namespace:MvvmUpperMachine"
Title="MVVM上位机" Height="300" Width="400">
<Window.DataContext>
<!-- 绑定ViewModel -->
<vm:MainViewModel />
</Window.DataContext>
<Grid Margin="20">
<StackPanel>
<!-- 温度显示:绑定ViewModel的SensorData.Temperature -->
<TextBlock Text="温度:" FontSize="16"/>
<TextBlock Text="{Binding SensorData.Temperature, StringFormat={} {0}℃}" FontSize="20" Margin="0,5"/>
<!-- 湿度显示:绑定ViewModel的SensorData.Humidity -->
<TextBlock Text="湿度:" FontSize="16" Margin="0,10"/>
<TextBlock Text="{Binding SensorData.Humidity, StringFormat={} {0}%}" FontSize="20" Margin="0,5"/>
<!-- 按钮:绑定ViewModel的命令 -->
<StackPanel Orientation="Horizontal" Margin="0,20">
<Button Content="开始采集" Command="{Binding StartCollectCommand}" Margin="0,0,10,0"/>
<Button Content="停止采集" Command="{Binding StopCollectCommand}"/>
</StackPanel>
</StackPanel>
</Grid>
</Window>
*/
// 4. 辅助类:RelayCommand(实现ICommand,用于按钮命令绑定)
public class RelayCommand : ICommand
{
private readonly Action _execute;
private readonly Func<bool> _canExecute;
public event EventHandler CanExecuteChanged;
public RelayCommand(Action execute, Func<bool> canExecute = null)
{
_execute = execute ?? throw new ArgumentNullException(nameof(execute));
_canExecute = canExecute;
}
public bool CanExecute(object parameter)
{
return _canExecute == null || _canExecute();
}
public void Execute(object parameter)
{
_execute();
}
public void RaiseCanExecuteChanged()
{
CanExecuteChanged?.Invoke(this, EventArgs.Empty);
}
}
优势:
- View里没有一行业务代码,UI布局调整只需改XAML,不影响逻辑;
- ViewModel专注业务逻辑,可单独进行单元测试(比如不启动UI,直接测试采集和解析逻辑);
- 数据绑定自动同步UI和ViewModel,避免手动调用
Invoke
更新UI,减少跨线程bug。
4. 单例模式:全局资源的“唯一管理者”
上位机痛点:配置管理、日志服务、设备管理器这些全局组件,如果被重复创建多个实例,会导致“配置读取冲突”“日志文件被占用”“设备多次连接”等问题。比如配置类被创建两次,一次读了config1.ini
,一次读了config2.ini
,程序行为混乱。
单例模式核心:确保一个类只有一个实例,并提供全局唯一的访问入口,避免重复创建资源。
代码实现:线程安全的单例模式(以上位机配置管理为例)
using System;
using System.IO;
public class AppConfig
{
// 1. 私有静态实例(volatile确保多线程下实例可见)
private static volatile AppConfig _instance;
// 2. 私有锁对象(保证线程安全)
private static readonly object _lockObj = new object();
// 3. 配置项
public string SerialPortName { get; private set; }
public int BaudRate { get; private set; }
public string TcpIp { get; private set; }
public int TcpPort { get; private set; }
// 4. 私有构造函数(禁止外部new)
private AppConfig()
{
// 从配置文件加载配置(只加载一次)
LoadConfig();
}
// 5. 公共静态方法:获取唯一实例(双重锁定保证线程安全)
public static AppConfig Instance
{
get
{
if (_instance == null) // 第一重判断(避免每次加锁)
{
lock (_lockObj) // 加锁
{
if (_instance == null) // 第二重判断(确保只创建一次)
{
_instance = new AppConfig();
}
}
}
return _instance;
}
}
// 加载配置文件(如app.config或自定义ini文件)
private void LoadConfig()
{
try
{
// 示例:从ini文件读取配置(实际项目可使用ConfigurationManager)
var configLines = File.ReadAllLines("app.ini");
foreach (var line in configLines)
{
if (line.StartsWith("SerialPortName:"))
SerialPortName = line.Split(':')[1].Trim();
else if (line.StartsWith("BaudRate:"))
BaudRate = int.Parse(line.Split(':')[1].Trim());
else if (line.StartsWith("TcpIp:"))
TcpIp = line.Split(':')[1].Trim();
else if (line.StartsWith("TcpPort:"))
TcpPort = int.Parse(line.Split(':')[1].Trim());
}
}
catch (Exception ex)
{
Console.WriteLine($"加载配置失败:{ex.Message},使用默认配置");
// 加载失败时使用默认配置
SerialPortName = "COM3";
BaudRate = 9600;
TcpIp = "192.168.1.100";
TcpPort = 502;
}
}
// 保存配置(全局唯一实例,避免多实例冲突)
public void SaveConfig()
{
var configContent = $"SerialPortName:{SerialPortName}\n" +
$"BaudRate:{BaudRate}\n" +
$"TcpIp:{TcpIp}\n" +
$"TcpPort:{TcpPort}";
File.WriteAllText("app.ini", configContent);
}
}
// 上层调用(全局唯一访问)
public class ConfigUsageDemo
{
public void TestConfig()
{
// 无论在哪调用,都是同一个实例
var config1 = AppConfig.Instance;
var config2 = AppConfig.Instance;
Console.WriteLine(ReferenceEquals(config1, config2)); // 输出True(同一实例)
// 使用配置创建设备通信
var serialComm = DeviceCommunicatorFactory.CreateCommunicator(
"serial", config1.SerialPortName, config1.BaudRate);
var tcpComm = DeviceCommunicatorFactory.CreateCommunicator(
"tcp", config1.TcpIp, config1.TcpPort);
}
}
优势:
- 全局组件(配置、日志、设备管理)只有一个实例,避免资源竞争和配置冲突;
- 线程安全的实现(双重锁定)确保多线程环境下不会创建多个实例;
- 配置加载只执行一次,提高程序启动效率。
5. 异步消息队列模式:高频数据的“削峰填谷”
上位机痛点:当传感器数据采集频率很高(如每秒1000次),直接在采集线程处理数据(解析、存储、UI更新)会导致“数据堆积”——采集速度超过处理速度,最终程序卡顿甚至崩溃。
异步消息队列模式核心:用一个“线程安全的队列”作为缓冲,采集线程(生产者)只负责将数据放入队列,处理线程(消费者)从队列中取出数据异步处理,实现“生产和消费解耦”,削峰填谷。
代码实现:基于ConcurrentQueue的异步消息队列
using System;
using System.Collections.Concurrent;
using System.Threading;
using System.Threading.Tasks;
public class AsyncMessageQueue<T>
{
// 线程安全的队列(ConcurrentQueue是.NET内置的线程安全队列)
private readonly ConcurrentQueue<T> _queue = new ConcurrentQueue<T>();
// 取消令牌(用于停止消费者线程)
private readonly CancellationTokenSource _cts = new CancellationTokenSource();
// 消费者处理函数(由外部传入)
private readonly Func<T, Task> _processFunc;
// 消费者任务(异步线程)
private Task _consumerTask;
// 构造函数:传入数据处理函数
public AsyncMessageQueue(Func<T, Task> processFunc)
{
_processFunc = processFunc ?? throw new ArgumentNullException(nameof(processFunc));
}
// 启动消费者(开始处理队列数据)
public void StartConsumer()
{
_consumerTask = Task.Run(async () => await ConsumerLoop(), _cts.Token);
}
// 停止消费者
public async Task StopConsumerAsync()
{
_cts.Cancel(); // 发送取消信号
if (_consumerTask != null)
{
await _consumerTask; // 等待消费者线程结束
}
}
// 生产者:将数据放入队列
public void Enqueue(T data)
{
if (_cts.Token.IsCancellationRequested)
throw new InvalidOperationException("队列已停止接收数据");
_queue.Enqueue(data); // 线程安全的入队操作
}
// 消费者循环:不断从队列取数据处理
private async Task ConsumerLoop()
{
while (!_cts.Token.IsCancellationRequested)
{
try
{
// 尝试从队列取数据(10ms超时,避免空等浪费CPU)
if (_queue.TryDequeue(out T data))
{
// 异步处理数据(不阻塞队列取数据)
await _processFunc(data);
}
else
{
// 队列空时,短暂等待
await Task.Delay(10, _cts.Token);
}
}
catch (OperationCanceledException)
{
// 取消操作,退出循环
break;
}
catch (Exception ex)
{
Console.WriteLine($"消费者处理数据失败:{ex.Message}");
}
}
}
}
// 上位机中的应用(高频数据采集场景)
public class HighFreqDataManager
{
private AsyncMessageQueue<byte[]> _dataQueue;
private DataCollector _dataCollector;
public void InitHighFreqCollect()
{
// 1. 创建消息队列:指定数据处理函数(异步处理)
_dataQueue = new AsyncMessageQueue<byte[]>(ProcessDataAsync);
// 2. 启动消费者线程
_dataQueue.StartConsumer();
// 3. 创建数据采集器,注册观察者(将数据入队)
var communicator = DeviceCommunicatorFactory.CreateCommunicator("serial", "COM3", 115200);
_dataCollector = new DataCollector(communicator);
_dataCollector.AddObserver(new QueueDataObserver(this));
// 4. 开始高频采集(每秒1000次)
_dataCollector.StartCollect();
}
// 观察者:将采集到的数据放入队列
private class QueueDataObserver : IDataObserver
{
private HighFreqDataManager _manager;
public QueueDataObserver(HighFreqDataManager manager)
{
_manager = manager;
}
public void OnDataReceived(byte[] data)
{
_manager._dataQueue.Enqueue(data); // 生产者入队
}
}
// 数据处理函数(异步,消费者调用)
private async Task ProcessDataAsync(byte[] data)
{
try
{
// 1. 解析数据(异步操作,如复杂计算)
var sensorData = await ParseDataAsync(data);
// 2. 保存到数据库(异步IO操作)
await SaveToDatabaseAsync(sensorData);
// 3. 更新UI(通过Dispatcher异步更新)
UpdateUiAsync(sensorData);
}
catch (Exception ex)
{
Console.WriteLine($"数据处理失败:{ex.Message}");
}
}
// 模拟异步数据解析
private Task<(int Temp, int Humi)> ParseDataAsync(byte[] data)
{
return Task.Run(() =>
{
int temp = BitConverter.ToInt16(data, 0);
int humi = BitConverter.ToInt16(data, 2);
return (temp, humi);
});
}
// 模拟异步保存数据库
private Task SaveToDatabaseAsync((int Temp, int Humi) data)
{
// 实际项目中使用EF Core等ORM的异步方法
return Task.Delay(5); // 模拟IO耗时
}
// 异步更新UI
private void UpdateUiAsync((int Temp, int Humi) data)
{
// WPF用Dispatcher,WinForm用Invoke
Application.Current.Dispatcher.Invoke(() =>
{
// 更新UI代码略
});
}
}
优势:
- 采集线程(生产者)只做“入队”这一轻量操作,不会被耗时的处理逻辑阻塞,确保数据不丢失;
- 处理线程(消费者)异步处理数据,即使处理速度慢,也只会在队列中堆积,不会导致采集线程卡死;
ConcurrentQueue
是线程安全的,避免多线程操作队列的锁竞争问题。
三、讨论:你在上位机开发中用对设计模式了吗?
设计模式不是“银弹”,但它是解决上位机“稳定性、可维护性”问题的有效工具。或许你曾在项目中“无意识”用过设计模式——比如用一个静态类管理配置(单例的雏形),用事件通知UI更新(观察者的简化版)。
但真正的架构能力,是“有意识地根据问题选择合适的模式”:
- 当需要统一管理多种设备通信时,用工厂模式;
- 当需要解耦数据生产者和消费者时,用观察者模式;
- 当需要分离UI和业务逻辑时,用MVVM模式;
- 当需要全局唯一资源时,用单例模式;
- 当需要处理高频数据时,用异步消息队列模式。
如果本文的设计模式案例对您有帮助,欢迎在评论区留言讨论:
- 您在工业上位机开发中遇到过哪些稳定性问题?
- 您对异步消息队列或MVVM模式有哪些疑问?
我会从优质评论中抽取5位读者,免费赠送《C#设计模式实战资料》!
------------伴代码深耕技术、连万物探索物联,我聚焦计算机、物联网与上位机领域,盼同频的你关注,一起交流成长~