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 实例的成员中)自动在范围内。这就是拥有会话对象的意义所在。

回到中止侦听器的主题,关闭流提供程序会触发回调,但尝试调用 EndRead 会导致IOException.

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

诀窍是在 中添加更多上下文 ( IsOpen) 以DeviceSession指示会话是打开还是已关闭,并使用它来顺利完成ReadStream.

If IsOpenis truethen anIOException表示需要恢复的故障。如果IsOpenfalse故意引发故障,则无需采取任何措施。

4

2 回答 2

2

[受到Richter's CLR via C#中关于APM的章节的启发,我决定看看SO在这个主题上有什么好东西,我发现了这个问题。我认为彼得在这里有一个很好的问题并做了一些研究——这就是结果]

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

该课程可以从上面的链接下载。该页面还包括一​​个指向 Yahoo Group Richter 的链接,该链接旨在为该库提供有限的支持。

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

使用 AsyncEnumerator 简化 APM
更多 AsyncEnumerator 功能

于 2010-10-06T05:39:33.720 回答
0

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

高温高压

科尔比非洲

于 2008-12-22T02:54:01.560 回答