Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/.net/20.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# SerialPort类偶尔挂起在Dispose上_C#_.net_Serial Port - Fatal编程技术网

C# SerialPort类偶尔挂起在Dispose上

C# SerialPort类偶尔挂起在Dispose上,c#,.net,serial-port,C#,.net,Serial Port,我编写了一个.net 4.0控制台应用程序,它定期与GSM调制解调器对话,以获取接收到的SMS消息列表(这是一个USB调制解调器,但代码通过串行端口驱动程序连接到它,并发送AT命令-顺便说一句,这是一个Sierra无线调制解调器,但我无法更改,我有最新的驱动程序)。经过一段时间(也许几个小时,也许几天),它就会停止工作。下面是一个日志片段 2012-04-17 23:07:31 DEBUG Modem Check (108) - Executing AT command 'AT+CPMS="ME

我编写了一个.net 4.0控制台应用程序,它定期与GSM调制解调器对话,以获取接收到的SMS消息列表(这是一个USB调制解调器,但代码通过串行端口驱动程序连接到它,并发送AT命令-顺便说一句,这是一个Sierra无线调制解调器,但我无法更改,我有最新的驱动程序)。经过一段时间(也许几个小时,也许几天),它就会停止工作。下面是一个日志片段

2012-04-17 23:07:31 DEBUG Modem Check (108) - Executing AT command 'AT+CPMS="ME"'...
2012-04-17 23:07:31 DEBUG Modem Check (108) - Finished executing 'AT+CPMS="ME"'
2012-04-17 23:07:31 DEBUG Modem Check (108) - Detaching event handlers for 'COM13'
2012-04-17 23:07:31 DEBUG Modem Check (108) - Disposing the SerialPort for 'COM13'
这就是日志的结尾-没有更多内容,尽管我希望看到至少一条语句,以下是相关代码:

internal T Execute()
{
    var modemPort = new SerialPort();
    T ret;

    try
    {
        modemPort.ErrorReceived += ModemPortErrorReceived;

        modemPort.PortName = _descriptor.PortName;
        modemPort.Handshake = Handshake.None;
        modemPort.DataBits = 8;
        modemPort.StopBits = StopBits.One;
        modemPort.Parity = Parity.None;
        modemPort.ReadTimeout = ReadTimeout;
        modemPort.WriteTimeout = WriteTimeout;
        modemPort.NewLine = "\r\n";
        modemPort.BaudRate = _descriptor.Baud;

        if (!modemPort.IsOpen)
        {
            modemPort.Open();
        }

        ret = _command.Execute(modemPort, _logger);

        _logger.Debug("Detaching event handlers for '{0}'",
                      _descriptor.PortName);

        modemPort.ErrorReceived -= ModemPortErrorReceived;

        _logger.Debug("Disposing the SerialPort for '{0}'",
                      _descriptor.PortName);
    }
    catch (IOException ex)
    {
        _logger.Error(ex.Message);

        throw new CommandException(
            string.Format(CultureInfo.CurrentCulture,
                          ModemWrapperStrings.COMMAND_ERROR,
                          ex.Message),
            ex);
    }
    catch (UnauthorizedAccessException ex)
    {
        _logger.Error(ex.Message);

        throw new CommandException(
            string.Format(CultureInfo.CurrentCulture,
                          ModemWrapperStrings.COMMAND_ERROR,
                          ex.Message),
            ex);
    }
    finally
    {
        modemPort.Dispose();

        _logger.Debug("Modem on port '{0}' disposed",
                      _descriptor.PortName);
    }

    return ret;
}
如您所见,它挂起在SerialPort类的Dispose方法上

我在谷歌上搜索了一下,发现了这个问题:从这个帖子:。代价似乎是在另一个线程中关闭端口,但这仅仅是用于表单应用程序吗?在我的例子中,我有一个简单的控制台应用程序,所以我认为它不适用(它只是在主线程中的一个循环中运行)。我甚至不确定这是否真的是这个问题(我的感觉是调制解调器的串行端口驱动程序很可能有问题,但我不知道,也许我对调制解调器不公平)。在我看来,我有三个选择:

  • 关闭其他线程中的端口
  • 在关闭端口之前进行延迟
  • 让港口永远开放
  • 我真的不喜欢这些解决办法,但我想让港口开放,看看会发生什么(我有这种感觉,它会泄漏内存或更糟,暴露出调制解调器的其他一些问题,但也许我只是悲观,如果是这样的话,我可能会每24小时关闭一次,比方说,然后重新打开它)所以我的问题是


    此代码是否存在可能导致此错误的其他问题,或者是否存在我上面概述的其他解决方法?

    SerialPort有点容易死锁。到目前为止,最常见的原因是您发现的,它是通过使用Invoke()触发的在DataReceived事件处理程序中。显然不是您的情况

    这些死锁与SerialPort在后台启动的工作线程有关。该线程有助于检测端口上的异步事件,底层本机winapi为WaitCommEvent()。该工作线程使DataReceived、Pinchhanged和ErrorReceived事件工作。请注意如何使用ErrorReceived

    Dispose()方法的作用与Close()方法相同,它向工作线程发出退出信号。但缺陷在于它不会等待线程退出。这是一个导致麻烦的方法,类似于MSDN文章中针对SerialPort.Close()在备注部分显式描述的:

    任何应用程序的最佳实践都是在调用Close方法后等待一段时间,然后再尝试调用Open方法,因为端口可能不会立即关闭

    坦白说,这是“最佳实践”中最糟糕的实践建议,因为它根本没有指定您应该等待的确切时间。有一个很好的理由,没有保证的安全值。等待一秒或两秒应该是99.9%良好。当机器负载很重,工作线程根本没有足够的周期来及时检测关闭状态时,会出现0.1%的故障模式。当然,这是绝对不可质疑的


    解决这个问题,只在程序开始时打开一个串行端口,退出时将其关闭。除了线程问题外,这还可以确保当另一个程序跳入并偷走端口时,不会随机失去对该端口的访问。请注意,实际上不再需要关闭端口,Windows将不再需要如果不使用,请小心。

    如果正在使用串行端口对象中的DataReceived事件或任何其他事件,则应在处理串行端口之前从中删除事件处理程序

    mySerial.DataReceived -= DataReceivedHandler;
    mySerial.Dispose();
    
    挂起是因为您在已处理的对象上触发了一个事件…这显然是一个bug

    但是,在您的情况下,您已经这样做了。挂起是因为端口尚未关闭。thread.sleep可能允许端口在尝试重新打开之前“稳定”。它可能也与硬件有关…这就是为什么没有最佳实践的原因

    与窗体控件相同:

    在_command.Execute(..)中发生了什么?您尝试过使用建议的解决方法吗?我知道所有的示例都是Winforms,但都非常清楚地描述了这个问题。至少值得一试。它只是发送命令并返回响应(在本例中,它以+CPMS=“ME”发送)-从那次通话中说的很好,因为我得到了“处理串行端口”记录消息,所以我认为它与它在那里实际做的事情不太相关?我是否可以做一些会导致挂起的事情?好的,所以它只是一个单线程方法,不会产生新线程来进行任何类型的异步通信。@M.Babcock还没有尝试它们-我将添加代码以在不同的线程中关闭d并在dispose操作之前延迟一段时间,看看会发生什么。谢谢,好消息-我将使用选项3,然后看看会发生什么!顺便说一句,如果我只是不关心ErrorReceived事件(这是否意味着该后台线程根本不会启动)?它挂在墙上,不是开着的,所以我想延迟的事情不适用,是吗?