如果您有待处理的操作,例如
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
.
If IsOpen
is true
then anIOException
表示需要恢复的故障。如果IsOpen
是false
故意引发故障,则无需采取任何措施。