Класс SerialPort иногда зависает на Dispose



Я написал консольное приложение .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 = "rn";
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;
}


Как вы можете видеть, он висит на методе Dispose класса SerialPort.



Я немного погуглил и пришел к этой проблеме: Serial Port Close зависает приложение из этого потока: serial port зависает при закрытии . Кажется, что consensious закрывает порт в другом потоке, но это только для приложения forms? В моем случае у меня есть простое консольное приложение, поэтому я не думаю, что оно применимо (оно просто работает в цикле в основном потоке). Я даже не уверен, что это на самом деле эта проблема (мое чувство, что это более вероятно, что есть проблема с драйвером последовательного порта от модема, но я не знаю, и, возможно, я несправедлив к модему). Насколько я понимаю, у меня есть три варианта:




  1. закройте порт в другом потоке

  2. поставьте задержку перед закрытием порта

  3. покиньте порт открой навсегда


Мне не очень нравятся эти обходные пути, но я думаю оставить порт открытым и просто посмотреть, что произойдет (у меня такое чувство, что это приведет к утечке памяти или, хуже того, выявит какую-то другую проблему с модемом, но, возможно, я просто пессимистичен, и если это так, я, вероятно, смогу закрыть его каждые 24 часа, скажем, и снова открыть его), поэтому мой вопрос таков...



Существует ли альтернативная проблема с этим кодом, которая может быть причиной этого bevahior или есть альтернативный обходной путь к тому, что я изложил выше?

630   2  

2 ответов:

SerialPort несколько склонен к взаимоблокировке. Безусловно, наиболее распространенной причиной является та, которую вы нашли, она запускается с помощью Invoke() в обработчике событий DataReceived. Здесь явно не твое дело.

Эти тупики связаны с рабочим потоком, который SerialPort запускает за занавесом. Этот поток помогает обнаруживать асинхронные события на порту, базовым собственным winapi является WaitCommEvent (). Этот работник заставляет работать события DataReceived, PinChanged и ErrorReceived. Обратите внимание, как Вы делаете Использование ErrorReceived.

Метод Dispose() делает то же самое, что и метод Close (), он сигнализирует рабочему потоку о выходе. Недостаток, однако, заключается в том, что он не ждет выхода потока. Это рецепт неприятностей, который явно описан в статье MSDN для SerialPort.Закрыть () в разделе Примечания:

Лучше всего для любого приложения подождать некоторое время после вызова метода Close, прежде чем попытка вызвать метод Open, так как порт может быть закрыт не сразу.

Что, откровенно говоря, является наихудшей возможной практикой для Совета "лучшая практика", поскольку он вообще не указывает, как долго вы должны ждать. По уважительной причине не существует гарантированной безопасной стоимости. Ожидание секунды или двух должно быть на 99,9% хорошим. Режим отказа 0.1% происходит, когда машина сильно загружена и рабочий поток просто не получает достаточного количества циклов для обнаружения закрытия. состояние во времени. Конечно, совершенно не поддается проверке.

Punt эта проблема, только когда-либо открыть последовательный порт в начале вашей программы и закрыть его при выходе. Это также гарантирует, что вы случайно не потеряете доступ к порту, когда другая программа прыгнет и украдет порт у вас. И обратите внимание, что закрывать порт на самом деле больше не нужно, Windows позаботится об этом, если вы этого не сделаете.

Если вы используете событие DataRecieved или любые другие события из объекта последовательного порта, вы должны удалить из него обработчик(ы) событий перед удалением последовательного порта.

mySerial.DataReceived -= DataReceivedHandler;
mySerial.Dispose();

Зависание происходит потому, что у вас есть событие, запускающее на удаленном объекте... что, очевидно, является ошибкой.

Однако, в вашем случае вы сделали это.. а зависание происходит потому, что порт не закрылся. возможно, нить.сон может позволить порту "успокоиться", прежде чем вы попытаетесь снова открыть его. Его, вероятно, аппаратная специфика также... вот почему нет лучшей практики.

То же самое, что и для управления формами: Как удалить все обработчики событий из элемента управления

Comments

    Ничего не найдено.