You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.10.13 15:24:01