首页 > 解决方案 > C# winforms 冻结:串口、定时器、线程

问题描述

编辑:保持原始问题的连续性。然后,我改用 ReadExisting 用 ReadLine() 方法的替换代码编辑了问题。它可以工作,但是我仍然有同样的冻结,应用程序变得无响应。调试说它在我等待完整消息的 while () {} 函数中锁定(冻结需要一段时间,有时是几秒钟,有时是几分钟)。更多解释如下:

- 过时的 -

处理 serialport.readtimeout 异常的好方法是什么?

                    try
                    {
                        serialPort1.Write(Command_);
                        if (!IsWriteComm_)
                        {
                            Response_ = serialPort1.ReadLine().Replace("\r", "");
                        }
                    }
                    catch (TimeoutException err)
                    {
                        DateTime d = DateTime.Now;
                        rtboxDiag.AppendText("\n" + d.ToString("HH:mm:ss") + ": ");
                        rtboxDiag.AppendText(err.Message);
                        if (!serialPort1.IsOpen)
                            InitConnection();
                        return Textbox_;
                    }

这段代码在计时器滴答事件上执行。我遇到了一个奇怪的应用程序“崩溃”,出现 IO 异常

“由于线程退出或应用程序请求,I/O 操作已中止。”

无论我做什么,我都无法“恢复”意思,我不再能够从串口轮询数据。

我添加了这个异常捕获,它确实记录了异常。奇怪的是,对 !serialport.isopen 的测试是错误的(意味着端口仍然打开)。

可能是一个提示:这个错误会以某种方式停止计时器,这不是我在代码中做的事情。所以我怀疑与定时器有关的东西,而不是串口,但我可能是错的。

手动关闭端口并重新连接并不能解决问题。断开并重新连接 USB 并不能解决问题。

但是,关闭应用程序并重新启动应用程序确实可以解决问题(甚至无需断开 MCU 或重启 MCU/硬件)。

- /过时的 -

编辑:问题在几秒钟后出现,有时是几分钟的完美操作。我不能使用串行端口终端以相同的方式以相同的频率轮询数据来重复该问题。看来问题不是来自硬件本身。

干杯

编辑:我还没有测试以下修改,不确定它是否会解决这个问题(我怀疑),但至少它是不使用 .readline() 的尝试,从我收集的信息来看这不是好的做法。

无论如何,它是:

                    try
                    {
                        serialPort1.Write(Command_);
                        if (!IsWriteComm_)
                        {
                            while (!SerialRxCplt) ;
                            Response_ = SerialRxResponse.Replace("\r", "").Replace("\n", "");
                            SerialRxCplt = false;
                            //Response_ = serialPort1.ReadLine().Replace("\r", "");
                        }
                    }
                    catch (TimeoutException err)
                    {
                        DateTime d = DateTime.Now;
                        rtboxDiag.AppendText("\n" + d.ToString("HH:mm:ss") + ": ");
                        rtboxDiag.AppendText(err.Message);
                        if (!serialPort1.IsOpen)
                            InitConnection();
                        return Textbox_;
                    }

我启用了 datareceived 事件:

private void serialPort1_DataReceived(object sender, System.IO.Ports.SerialDataReceivedEventArgs e)
        {
            var serialPort = (System.IO.Ports.SerialPort)sender;
            string dataReceived = serialPort.ReadExisting();
            ProcessSerialData(dataReceived);
        }

这就是我处理数据的方式,并手动“等待” \n 字符,它告诉我何时完全接收到数据。

private void ProcessSerialData(string data)
        {
            SerialRxBuffer += data;
            if (SerialRxBuffer.Contains("\n"))
            {
                SerialRxCplt = true;
                SerialRxResponse = SerialRxBuffer;
                SerialRxBuffer = "";
            }
            else
            {
                SerialRxCplt = false;
            }
        }

欢迎任何意见。

我在那个while循环中添加了用于调试的“东西”,它确实可以正常工作一段时间然后冻结,那里没有抛出错误或异常。出于某种原因,我觉得它与串行端口无关。我什至添加了这个:

                    try
                    {
                        serialPort1.Write(Command_);
                        if (!IsWriteComm_)
                        {
                            Stopwatch stopWatch = new Stopwatch();
                            stopWatch.Start();
                            while (!SerialRxCplt || Timer2StopWatchMilli > 5)
                            {
                                Timer2StopWatchMilli = stopWatch.Elapsed.TotalMilliseconds;
                                ExceptionMessage = Timer2StopWatchMilli.ToString();
                                IsException = true;
                            }
                            stopWatch.Stop();
                            if (!SerialRxCplt)
                                return Textbox_;
                            Response_ = SerialRxResponse.Replace("\r", "").Replace("\n", "");
                            SerialRxCplt = false;
                            //Response_ = serialPort1.ReadLine().Replace("\r", "");
                        }
                    }

ExceptionMessage 和 IsException 帮助我了解该循环中发生了什么。在正常操作中,它是您所需要的,以 0.0x 毫秒的数量级递增。正在正确处理数据。当它冻结时,看起来没有任何异常。我最初以为我以某种方式“卡”在无限循环中,但 || Timer2StopWatchMilli > 5 应该让我摆脱它,充当某种超时。

一条额外的信息:当它冻结时,一个 CPU 内核已满载。(我有一个 6 核 CPU,它在任务管理器中是 16-17% - 内存使用率低 < 30MB)

欢迎任何帮助

标签: c#winformstimer

解决方案


我通过在每次成功交易后清除 RX/TX 和流缓冲区来修复它。我认为数据发送到 PC 的速度比它能够读取的速度更快,导致数据最终累积在 Rx 缓冲区中。

private void SerialPortClearBuffers()
        {
            serialPort1.DiscardOutBuffer();
            serialPort1.DiscardInBuffer();
            serialPort1.BaseStream.Flush();
        }

推荐阅读