Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
466 views
in Technique[技术] by (71.8m points)

c# - SerialPort class occasionally hangs on Dispose

I have written a .net 4.0 console application which periodically talks to a GSM modem to get a list of the receieved SMS messages (it is a USB modem but the code connects to it via a serial port driver and sends AT commands - incidentally it is a Sierra Wireless modem but I can't change it and I have the latest driver). What happens is that after some period of time (maybe hours, maybe days) it just stops working. Here is a log snippet...

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'

That is the end of the log - nothing more even though I would expect to see at least one more statement, here is the relevant code:

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 = "
";
        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;
}

As you can see it hangs on the Dispose method of the SerialPort class.

I did some Googling and I came to this issue: Serial Port Close Hangs the application from this thread: serial port hangs whilst closing. The consensious seems to be to close the port in a different thread but is that just for a forms application? In my case I have a simple console application so I do not think it applies (it is just running in a loop in the main thread). I am not even sure it is actually this issue (my feeling is that it is more likely that there is an issue with the serial port driver from the modem but I don't know and perhaps I am being unfair to the modem). As far as I see it I have three choices:

  1. Close the port in a different thread
  2. Put in a delay before closing the port
  3. Leave the port open forever

I don't really like any of those workarounds but I am thinking of leaving the port open and just seeing what happens (I have this feeling that it will leak memory or worse, expose some other issue with the modem but maybe I am just pessimistic and if that is the case I could probably get away with closing it every 24 hours, say, and re-opening it again) so my question is...

Is there an alternative problem with this code that could be causing this bevahior or is there an alternative workaround to what I have outlined above?

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

SerialPort is somewhat prone to deadlock. By far the most common cause is the one you found, it is triggered by using Invoke() in a DataReceived event handler. Clearly not your case here.

These deadlocks are related to a worker thread that SerialPort starts behind the curtain. That thread helps to detect asynchronous events on the port, the underlying native winapi is WaitCommEvent(). That worker makes the DataReceived, PinChanged and ErrorReceived events work. Note how you do use ErrorReceived.

The Dispose() method does the same thing as the Close() method, it signals the worker thread to exit. The flaw however is that it doesn't wait for the thread to exit. That's a recipe for trouble, the kind that is explicitly documented in the MSDN article for SerialPort.Close() in the Remarks section:

The best practice for any application is to wait for some amount of time after calling the Close method before attempting to call the Open method, as the port may not be closed instantly.

Which is, frankly, the worst possible practice for "best practice" advice since it doesn't at all specify exactly how long you are supposed to wait. For a good reason, there is no guaranteed safe value. Waiting for a second or two should be 99.9% good. The 0.1% failure mode occurs when the machine is heavily loaded and the worker thread simply doesn't get enough cycles to detect the close condition in time. Utterly undebuggable of course.

Punt this problem, only ever open a serial port at the start of your program and close it when exiting. Short from threading trouble, this also ensures that you don't randomly lose access to the port when another program jumps in and steals the port away from you. And note that closing the port isn't actually necessary anymore, Windows will take care of it if you don't.


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...