Как убить ожидающую операцию APM - PullRequest
2 голосов
/ 22 декабря 2008

Если у вас есть ожидающая операция, например,

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, то ошибка была намеренно вызвана, и никаких действий не требуется.

Ответы [ 2 ]

2 голосов
/ 06 октября 2010

[Вдохновленный главой о APM в CLR Рихтера через C #, я решил посмотреть, какие вкусности имел SO по этому вопросу, и нашел этот вопрос. Я подумал, что у Питера был отличный вопрос, и он провел небольшое исследование - это результат]

Джеффри Рихтер в CLR через C # (глава 27) обсуждает свой класс AsyncEnumerator, который (предположительно) избавляет от боли при программировании APM. Одной из особенностей этого класса (часть его свободно доступного Power Threading Libary ) является возможность отмены асинхронных операций.

Класс можно скачать по ссылке выше. На этой странице также есть ссылка на Yahoo Group Richter, настроенную для предоставления ограниченной поддержки библиотеки.

Он представляет библиотеку в следующих статьях MSDN:

Упрощенный APM с помощью AsyncEnumerator
Дополнительные функции AsyncEnumerator

0 голосов
/ 22 декабря 2008

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

НТН

Колби Африка

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...