Como eliminar uma operação APM pendente
Se você tiver uma operação pendente, por exemplo,
stream.BeginRead(_buffer, 0, _buffer.Length, _asyncCallbackRead, this);
e você fecha o provedor de stream, por exemplo
serialPort.Close();
você surpreendentemente causa uma exceção.
Existe um método preferido pelo qual se pode cancelar uma operação APM pendente antes de fechar a porta?
A resposta de Colby não é a resposta que eu esperava, mas ele fecha pelo menos uma avenida infrutífera no inquérito.
Felizmente encontrei uma solução.
Para cada fluxo, mantenho várias informações de estado em uma classeDeviceSession
. Esta classe tem um métodoReadStream
fornecendo a implementação para oAsyncCallback
que lida com dados recebidos.
Observe que_asyncCallbackRead
e todas as outras variáveis que começam com um sublinhado são um membro privado de classe atribuído no construtor de DeviceSession.
O construtor também fornece a chamada inicial para_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);
}
}
Observe que não me preocupei em definirar.AsyncState
. Como o delegado de retorno de chamada se refere ao método de uma instância específica do DeviceSession, informações detalhadas e fortemente tipadas do contexto (contidas nos membros dessa instância do DeviceSession) sãoautomaticamente na mira. Este é o ponto de ter um objeto de sessão.
De volta ao assunto de abortar um ouvinte, o fechamento do provedor de fluxo aciona o retorno de chamada, mas a tentativa de chamar os resultados do EndRead em umIOException
.
Geralmente, essa exceção indica uma falha que requer a reinicialização do ouvinte e é recomendável responder reiniciando o provedor de fluxo e recriando a sessão. Isso é complicado pela ausência de uma maneira confiável e independente do provedor de fluxo para determinar se o provedor falhou ou o usuário está tentando reiniciar a conexão (por exemplo, conectou um novo dispositivo à porta).
O truque é adicionar mais contexto (IsOpen
) aoDeviceSession
para indicar se a sessão está aberta ou foi fechada e use-a para concluir sem problemas a execução final abortiva deReadStream
.
E seIsOpen
étrue
então umIOException
representa uma falha na necessidade de recuperação. E seIsOpen
éfalse
a falha foi induzida deliberadamente e nenhuma ação é necessária.