如果您有待处理的操作,例如
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实例的成员中)在范围内自动。这是拥有会话对象的重点。
回到中止侦听器的主题,关闭流提供程序会触发回调,但尝试在IOException
中调用EndRead结果。
通常,此类异常表示需要重新启动侦听器的故障,并且需要通过重新启动流提供程序并重新创建会话来进行响应。由于缺少可靠的与流提供者无关的方式来确定提供商是否出现故障或用户是否正在尝试重新启动连接(例如,将新设备插入端口),这很复杂。
诀窍是向IsOpen
添加更多上下文(DeviceSession
)以指示会话是打开还是已关闭,并使用它来顺利完成{{1的最终中止执行}}
如果ReadStream
为IsOpen
,则true
表示无法恢复。如果IOException
为IsOpen
,则会故意诱导失败,无需采取任何措施。
答案 0 :(得分:2)
[灵感来自Richter CLR中的APM章节通过C#我决定看看SO在这个主题上有什么好处,我发现了这个问题。我认为彼得在这里有一个很好的问题并做了一些研究 - 这就是结果]
CLR中的Jeffrey Richter通过C#(第27章)讨论了他的AsyncEnumerator类,它(据说)从编程APM中消除了很多痛苦。这个类的一个特性(他可以免费获得Power Threading Libary的一部分)是取消异步操作的能力。可以从上面的链接下载课程。该页面还包含一个指向Yahoo Group Richter的链接,该链接设置为lib提供有限的支持。
他在这些MSDN文章中介绍了这个库:
Simplified APM With The AsyncEnumerator
More AsyncEnumerator Features
答案 1 :(得分:0)
框架中不直接支持此功能。最好的办法是编写一个生成线程的包装器,并使用事件等同步原语来表示取消请求。
HTH
非洲科尔比