明确地实现IAsyncResult

我一般都对部分实现接口持谨慎态度。 但是, IAsyncResult有点特殊情况,因为它支持几种截然不同的使用模式。 使用/看到使用AsyncState / AsyncCallback模式的AsyncStateAsyncState ,而不是只使用AsyncWaitHandle调用EndInvoke ,或者轮询IsCompletedAsyncWaitHandle )?

相关问题: 检测ThreadPool WorkItem是否已完成/等待完成 。

考虑这个类(非常近似,需要锁定):

 public class Concurrent { private ManualResetEvent _resetEvent; private T _result; public Concurrent(Func f) { ThreadPool.QueueUserWorkItem(_ => { _result = f(); IsCompleted = true; if (_resetEvent != null) _resetEvent.Set(); }); } public WaitHandle WaitHandle { get { if (_resetEvent == null) _resetEvent = new ManualResetEvent(IsCompleted); return _resetEvent; } public bool IsCompleted {get; private set;} ... 

它有WaitHandle (懒洋洋地创建,就像在IAsyncResult文档中描述的那样)和IsCompleted ,但我没有看到AsyncState的合理实现( {return null;} ?)。 那么它实现IAsyncResult是否有意义? 请注意,Parallel Extensions库中的Task确实实现了IAsyncResult ,但只隐式实现了IsCompleted

  • 根据我的经验,只是在没有等待或首先被回调的情况下调用EndInvoke很少有用
  • 仅提供回调有时是不够的,因为您的客户可能希望一次等待多个操作(WaitAny,WaitAll)
  • 我从未对IsCompleted进行过调查,确实如此! 因此,您可以保存IsCompleted的实现,但它非常简单,似乎不值得让您的客户感到惊讶。

因此,异步可调用方法的合理实现应该真正提供完全实现的IAsyncResult。

顺便说一句,您通常不需要自己实现IAsyncResult,只需返回Delegate.BeginInvoke返回的内容即可。 有关示例,请参阅System.IO.Stream.BeginRead的实现。

看起来你有几个问题。 让我们分别处理它们

懒洋洋地创建WaitHandle

是的,这是最正确的方法。 你应该以线程安全的方式做到这一点,但懒惰是这样的。

但诀窍是处理WaitHandle。 WaitHandle是IDisposable的基础, 必须及时处理。 IAsycResult的文档不包括这种情况。 最好的方法是在EndInvoke中。 BeginInvoke的文档明确指出,对于每个BeginInvoke,必须有相应的EndInvoke(或BeginRead / EndRead)。 这是处理WaitHandle的最佳位置。

应该如何实现AsyncState?

如果你看一下返回IAsyncResult的标准BCL API,它们中的大多数采用一个状态参数。 这通常是从AsyncState返回的值(有关示例,请参阅Socket API)。 对于任何返回IAsyncResult的API BeginInvoke样式API,包含一个类型为对象的状态变量是一个好习惯。 没有必要,但良好的做法。

在状态变量的abscence中,返回null是可以接受的。

IsCompleted API

这将高度依赖于创建IAsyncResult的实现。 但是,是的,你应该实现它。