我已经编写了一个.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方法上。

我做了一些谷歌搜索,然后我从这个线程Serial Port Close Hangs the application来到了这个问题:serial port hangs whilst closing。共识似乎是在另一个线程中关闭端口,但这仅适用于表单应用程序吗?就我而言,我有一个简单的控制台应用程序,因此我认为它不适用(它仅在主线程中循环运行)。我什至不确定这实际上是这个问题(我的感觉是调制解调器的串行端口驱动程序很有可能出现问题,但我不知道,也许我对调制解调器不公平)。据我所知,我有三个选择:
  • 在另一个线程中关闭端口
  • 延迟关闭端口
  • 之前
  • 永远保持端口开放

  • 我真的不喜欢这些解决方法,但是我正在考虑将端口保持打开状态,然后看看会发生什么情况(我有种感觉,那就是它会泄漏内存或变得更糟,暴露出调制解调器的其他问题,但也许我只是悲观的如果是这种情况,我大概可以每隔24小时关闭一次,然后再次重新打开),所以我的问题是...

    此代码是否有其他问题可能引起这种现象,还是我在上面概述的问题有替代的解决方法?

    最佳答案

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

    这些死锁与SerialPort在幕后启动的工作线程有关。该线程有助于检测端口上的异步事件,底层的本地winapi是WaitCommEvent()。该工作程序使DataReceived,PinChanged和ErrorReceived事件起作用。注意如何使用ErrorReceived。

    Dispose()方法执行与Close()方法相同的操作,它发出工作线程退出的信号。但是,缺点是它不等待线程退出。这是麻烦的秘诀,在“备注”部分的SerialPort.Close()的MSDN文章中明确记录了这种麻烦:



    坦率地说,这是“最佳实践”建议的最坏实践,因为它根本没有确切说明您应该等待多长时间。有充分的理由,没有保证的安全值。等待一两秒钟应该是99.9%的好。当机器负载很重并且工作线程根本没有足够的周期来及时检测关闭状态时,就会发生0.1%失败模式。当然绝对不能容忍。

    提出这个问题,只能在程序开始时打开一个串行端口,并在退出时将其关闭。由于没有线程问题,这还可以确保您不会在其他程序跳入并从您那里抢走端口时随机失去对端口的访问权限。请注意,实际上不再需要关闭端口,如果不需要,Windows会照顾好它。

    关于c# - SerialPort类偶尔会卡在Dispose上,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/10209090/

    10-11 19:40