如何终止挂起的APM操作

问题描述 投票:2回答:2

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

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)以指示会话是打开还是已关闭,并使用它来顺利完成ReadStream的最终中止执行。

如果IsOpentrue那么IOException代表需要恢复的失败。如果IsOpenfalse,则故意引发失败并且不需要采取任何行动。

c# .net asynchronous
2个回答
0
投票

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

HTH

科尔比非洲


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

© www.soinside.com 2019 - 2024. All rights reserved.