c#在关闭串口时卡死

文章描述了使用C#SerialPort类操作串口时遇到的问题,即在串口关闭时程序卡死。作者通过分析发现,主要问题是UI线程与DataReceived事件处理程序之间的线程同步问题。通过将Invoke改为BeginInvoke,解决了线程死锁,使得串口在无数据接收时能正常关闭并释放资源。
摘要由CSDN通过智能技术生成

        使用c#提供的serialport类操作串口,串口的配置和回调函数的声明定义如下:

 private SerialPort _EPL1023DAObj

_EPL1023DAObj.PortName = EPL1023DA_SerialPortNameCombox.Text;         
_EPL1023DAObj.BaudRate = int.Parse(EPL1023DA_SerialPortBaudRateCombox.Text);
_EPL1023DAObj.DataBits = int.Parse(EPL1023DA_SerialPortDataBitCombox.Text);
switch (EPL1023DA_SerialPortStopBitCombox.SelectedIndex)
            {
                case 0:
                    _EPL1023DAObj.StopBits = StopBits.One;
                    break;
                case 1:
                    _EPL1023DAObj.StopBits = StopBits.OnePointFive;
                    break;
                case 2:
                    _EPL1023DAObj.StopBits = StopBits.Two;
                    break;
            }
 _EPL1023DAObj.Encoding = Encoding.GetEncoding("UTF-8");
 _EPL1023DAObj.DataReceived += _EPL1023DASession_DataReceived;

        private void _EPL1023DASession_DataReceived(object sender, SerialDataReceivedEventArgs e)
        {

            string recData = "";
            if (!_EPL1023DAObj.IsOpen)
                return;
            try
            {
                if (_EPL1023DAObj != null && _EPL1023DAObj.IsOpen)
                     recData = _EPL1023DAObj.ReadLine();
                  //  recData = _EPL1023DAObj.ReadExisting().ToString();
            }
            catch (TimeoutException) {
                return;
            }

            if (_EPL1023AMFlag)
            {
                // 测试过程中 按照逗号解析  接收数据格式:"[命令],[数据]"
                string[] recStr = recData.Split(',');
                double.TryParse(recStr[0], out _EPL1023DAUncert);
                _EPL1023DAUncertGetFlag = true;
            }
            else
            {
                this.Invoke(new Action(() =>
                {
                    if(recData!="")
        EPL1023DA_DataShowTxtbox.AppendText(_EPL1023DAObj.PortName + ":" + recData);
                }));


            }

        这版代码在串口关闭时会直接卡死在 recData = _EPL1023DAObj.ReadLine(); 处,直接关闭软件时也会卡死,即使在关闭窗口的回调函数中增加了串口中断回调函数的解绑,串口缓存区的清除,串口资源的释放等操作,依然会导致软件卡死。

        后根据个人猜测,可能和调用的ReadLine函数有关,遂将该函数替换为ReadExisting后,可以调用close将串口正常关闭了,但是如果不单独将串口关闭后再关闭窗口 依然会导致程序卡死。即使在窗口关闭的回调函数中调用关闭串口按钮的回调函数依然会导致串口卡死。以上现象均是建立在应用程序一直在接收数据的情况下,如果串口无数据接收,可以正常关闭串口和程序。

        根据分析底层可能是UI界面同DataReceived事件处理程序是在lock (stream) {…}块中执行的SerialPort实例执行Close()方法时会先关闭SerialPort实例内部的SerialStream实例
SerialStream实例执行Close()方法时会lock实例自身当辅助线程调用DataReceived事件处理程序处理串口数据但还未更新界面时,点击界面“关闭”按钮调用SerialPort实例的Close()方法,UI线程会在lock(stream)处一直等待辅助线程释放stream的线程锁。当辅助线程处理完数据准备更新界面时问题来了,DataReceived事件处理程序中的this.Invoke()一直会等待UI线程来执行委托,但此时UI线程还停在SerialPort实例的Close()方法处等待DataReceived事件处理程序执行完成。
此时,线程死锁发生,两边都执行不下去了。遂将this.Invoke修改this.BeginInvoke,不等待UI线程执行完委托就返回,stream的线程锁会很快释放,SerialPort实例的Close()方法也无需等待

       private void _EPL1023DASession_DataReceived(object sender, SerialDataReceivedEventArgs e)
        {

            string recData = "";
            if (!_EPL1023DAObj.IsOpen)
                return;
            try
            {
                if (_EPL1023DAObj != null && _EPL1023DAObj.IsOpen)
                     //recData = _EPL1023DAObj.ReadLine();
                    recData = _EPL1023DAObj.ReadExisting().ToString();
            }
            catch (TimeoutException) {
                return;
            }

            if (_EPL1023AMFlag)
            {
                // 测试过程中 按照逗号解析  接收数据格式:"[命令],[数据]"
                string[] recStr = recData.Split(',');
                double.TryParse(recStr[0], out _EPL1023DAUncert);
                _EPL1023DAUncertGetFlag = true;
            }
            else
            {/*
                this.Invoke(new Action(() =>
                {
                    if(recData!="")
                    EPL1023DA_DataShowTxtbox.AppendText(_EPL1023DAObj.PortName + ":" + recData);
                }));
                */
                this.BeginInvoke(new Action(() => {
                    if (recData != "")
                        EPL1023DA_DataShowTxtbox.AppendText(_EPL1033DAObj.PortName + ":" + recData);
                }));

            }


        }

  • 7
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
C#中的循环多线程在界面上运行可能会导致卡死的问题。这是因为C#程序在运行会先处理代码,代码运行完后再渲染到界面上。所以当一个循环运行,界面可能会无响应。为了解决这个问题,有一种方法是在循环中使用Thread.sleep(0),阻塞当前线程,让出间片给UI线程。然而,仅仅使用Thread.sleep(0)是不够的,因为当前线程太耗,总是会立即抢占回间片。所以,无论使用多少秒的睡眠都无法解决问题。为了避免循环多线程卡死,可以考虑使用异步编程模型,例如使用async和await关键字来处理循环任务。这样可以将循环任务放在一个独立的线程中执行,避免阻塞UI线程,同可以实现界面的正常响应。另外,还可以考虑使用Task类和Parallel类来处理循环任务,以提高程序的性能和效率。总之,通过合理的线程管理和使用异步编程模型,可以解决C#循环多线程卡死的问题,并确保界面的正常运行。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* *2* [C#windows项目解决线程卡死问题](https://blog.csdn.net/qq_41712950/article/details/124661448)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_1"}}] [.reference_item style="max-width: 50%"] - *3* [C#多线程加载控件界面卡死的解决](https://blog.csdn.net/u012999461/article/details/122501744)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_1"}}] [.reference_item style="max-width: 50%"] [ .reference_list ]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值