如何终止挂起的APM操作

如果您有待处理的操作,例如

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

通常,此类exception表示需要重新启动侦听器的故障,并且需要通过重新启动流提供程序并重新创建会话来进行响应。 由于缺乏可靠的与流提供者无关的方式来确定提供者是否出现故障或用户是否正在尝试重新启动连接(例如,将新设备插入端口),这很复杂。

诀窍是向DeviceSession添加更多上下文( IsOpen )以指示会话是打开还是已关闭,并使用它来顺利完成ReadStream的最终中止执行。

如果IsOpentrueIOException表示需要恢复失败。 如果IsOpenfalse ,则故意引发故障并且不需要采取任何措施。

框架中不直接支持这一点。 最好的办法是编写一个生成线程的包装器,并使用事件等同步原语来表示取消请求。

HTH

科尔比非洲

[灵感来自Richter CLR中的APM章节通过C#我决定看看SO在这个主题上有什么好处,我发现了这个问题。 我认为彼得在这里有一个很好的问题并做了一些研究 – 这就是结果]

CLR中的Jeffrey Richter通过C#(第27章)讨论了他的AsyncEnumerator类,它(据说)从编程APM中消除了很多痛苦。 这个类的一个特性(他免费提供的Power Threading Libary的一部分 )是取消异步操作的能力。

该课程可以从上面的链接下载。 该页面还包含一个指向Yahoo Group Richter的链接,该链接设置为lib提供有限的支持。

他在这些MSDN文章中介绍了该库:

使用AsyncEnumerator简化APM
更多AsyncEnumeratorfunction