Если у вас есть ожидающая операция, например,
stream.BeginRead(_buffer, 0, _buffer.Length, _asyncCallbackRead, this);
и вы закрываете потокового провайдера, например
serialPort.Close();
неудивительно, что вы вызываете исключение.
Есть ли предпочтительный метод, с помощью которого можно отменить ожидающую операцию APM до закрытия порта?
Ответ Колби - не тот ответ, на который я надеялся, но он, по крайней мере, закрывает бесплодный путь в расследовании.
К счастью, я нашел решение.
Для каждого потока я поддерживаю различную информацию о состоянии в классе DeviceSession
. Этот класс имеет метод ReadStream
, обеспечивающий реализацию AsyncCallback
, которая обрабатывает входящие данные.
Обратите внимание, что _asyncCallbackRead
и любая другая переменная, начинающаяся с подчеркивания, является закрытым членом класса, назначенным в конструкторе DeviceSession.
Конструктор также обеспечивает начальный вызов _stream.BeginRead
.
void ReadStream(IAsyncResult ar)
{
if (IsOpen)
try
{
DevicePacket packet;
int cbRead = _stream.EndRead(ar);
_endOfValidData += cbRead;
while ((packet = GetPacket()) != null)
CommandStrategy.Process(this, packet);
_stream.BeginRead(_buffer, _endOfValidData,
_buffer.Length - _endOfValidData,
_asyncCallbackRead, null);
}
catch (Exception ex)
{
Trace.TraceError("{0}\r\n{1}", ex.Message, ex.StackTrace);
_restart(_streamProvider, _deviceId);
}
}
Обратите внимание, что я не удосужился установить ar.AsyncState
. Поскольку делегат обратного вызова относится к методу конкретного экземпляра DeviceSession, подробная и строго типизированная контекстная информация (содержащаяся в элементах этого экземпляра DeviceSession) имеет в области автоматически . В этом и заключается смысл наличия объекта сеанса.
Возвращаясь к теме прерывания прослушивателя, закрытие поставщика потоков вызывает обратный вызов, но попытка вызвать EndRead приводит к IOException
.
Как правило, такое исключение указывает на ошибку, требующую перезапуска слушателя, и можно захотеть ответить, перезапустив потоковый провайдер и воссоздав сеанс. Это осложняется отсутствием надежного независимого от поставщика потокового способа определения того, провайдер отказал или пользователь пытается перезапустить соединение (например, подключил новое устройство к порту).
Хитрость заключается в том, чтобы добавить дополнительный контекст (IsOpen
) к DeviceSession
, чтобы указать, открыт ли сеанс или был закрыт, и использовать его для плавного завершения окончательного неудачного выполнения ReadStream
.
Если IsOpen
равно true
, то IOException
означает сбой, требующий восстановления. Если IsOpen
равно false
, то ошибка была намеренно вызвана, и никаких действий не требуется.