Arduino对接WPF时如何在DataReceivedHandler中实现不阻塞UI的延迟
核心问题说明
你的现有实现逻辑存在两个致命问题,导致无法达到预期效果:
- 你将延迟写在了串口
DataReceived事件回调中,串口有数据到达就会触发该事件,如果在事件中等待2秒再读数据,会导致串口接收缓冲区被未及时读取的数据占满,新接收的数据直接丢失 - 接收逻辑和显示逻辑强绑定,
DataReceived的触发频率由Arduino发送数据的速度决定,根本无法实现固定2秒间隔显示的需求
解决方案
采用接收与显示解耦的设计:用线程安全的队列缓存所有接收到的串口数据,单独启动异步任务消费队列控制显示间隔,既保证数据不丢失,也不会阻塞UI。
修改后的完整代码如下:
using System.Collections.Concurrent; using System.IO.Ports; using System.Windows; namespace SerialPortDemo { public partial class MainWindow : Window { private SerialPort mySerialPort; private readonly StreamWriter sw = new(@"D:\MAAT\readcoba.csv"); // 线程安全队列,用于缓存所有接收到的串口数据 private readonly ConcurrentQueue<string> _dataBuffer = new(); // 控制消费任务启停的标记 private bool _isRunning = true; public MainWindow() { InitializeComponent(); Configure(); // 启动数据消费任务 _ = ConsumeDataAsync(); } private void Configure() { mySerialPort = new SerialPort(Port()) { BaudRate = 9600, Parity = Parity.None, StopBits = StopBits.One, DataBits = 8, }; mySerialPort.DataReceived += DataReceivedHandler; mySerialPort.Open(); } private string Port() { string port = ""; string[] ports = SerialPort.GetPortNames(); foreach (string x in ports) { port = x; } return port; } private void DataReceivedHandler(object sender, SerialDataReceivedEventArgs e) { // 接收事件只做读取、写日志、入队操作,不添加任何延迟,保证数据不丢失 string indata = mySerialPort.ReadExisting(); sw.WriteLine(indata); _dataBuffer.Enqueue(indata); } private async Task ConsumeDataAsync() { while (_isRunning) { // 队列有数据就取出显示 if (_dataBuffer.TryDequeue(out string data)) { string[] arrData = data.Split(','); // 异步更新UI,不会阻塞 await Application.Current.Dispatcher.InvokeAsync(() => { foreach (string item in arrData) { raw_data_label.Content = raw_data_label.Content.ToString().Contains("Nothing to Display") ? $"{item}" : $"{raw_data_label.Content}{item}"; } }); // 显示完成后等待2秒再处理下一条 await Task.Delay(2000); } else { // 队列无数据时等待100ms再检查,避免空转占用CPU await Task.Delay(100); } } } private void WindowClosed(object sender, EventArgs e) { // 停止消费任务 _isRunning = false; sw.Close(); mySerialPort.Close(); } } }
关键修改说明
- 新增
ConcurrentQueue<string>作为数据缓冲,串口接收到的数据会立刻存入队列,不会因为显示延迟导致数据丢失 - 单独的
ConsumeDataAsync异步任务负责控制显示节奏,每处理完一条数据就等待2秒,完全符合你的间隔显示需求 - UI更新使用异步的
InvokeAsync方法,配合await不会阻塞UI线程,不会出现界面卡顿问题 - 写文件操作移到接收事件中执行,日志写入不会受显示延迟影响,保证所有数据都能完整落地
内容的提问来源于stack exchange,提问作者Sadamuhiz
相关产品推荐
相关产品推荐

