【问题标题】:Implementing IAsyncResult explicitly显式实现 IAsyncResult
【发布时间】:2009-01-02 10:02:36
【问题描述】:

我通常对部分实现接口持谨慎态度。但是,IAsyncResult 有点特殊,因为它支持几种完全不同的使用模式。您多久使用一次/看到使用AsyncState/AsyncCallback 模式,而不是仅仅调用EndInvoke、使用AsyncWaitHandle 或轮询IsCompleted(讨厌)?

相关问题:Detecting that a ThreadPool WorkItem has completed/waiting for completion

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

public class Concurrent<T> {
    private ManualResetEvent _resetEvent;
    private T _result;

    public Concurrent(Func<T> 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 是隐式实现的。

【问题讨论】:

    标签: c# concurrency asynchronous iasyncresult


    【解决方案1】:

    您似乎有几个问题。让我们单独处理它们

    懒惰地创建 WaitHandle

    是的,这是最正确的方法。您应该以线程安全的方式执行此操作,但懒惰是一种方式。

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

    AsyncState 应该如何实现?

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

    在没有状态变量的情况下,返回 null 是可以接受的。

    IsCompleted API

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

    【讨论】:

    • 我想知道是谁提出了在具有如此繁重实现的重要类中显式实现 IDisposable 的想法。在查看 Reflector 之后,我看不到 AsyncResult 在哪里处理其 WaitHandle 或在哪里设置了 EndInvokeCalled。我需要再考虑一下。
    • 在 EndInvoke 中,大概是(其实现对 Reflector 是不可见的)。
    • @alexey_r,查看 FileStread.EndRead。 Reflector 的诀窍是使用 IAsyncResult->Derived Types。选择一个派生类型,寻找 AsyncWaitHandle 的真正实现并查找所有引用。最终你会找到一个放置在 End*** 方法中的地方。
    • 谢谢!在我的场景中,我希望多个线程能够获取 WaitHandle 并等待它。 ManualResetEvent 不能很好地解决这个问题:如果我的类完成计算、处理它然后某个线程决定等待它,就会发生不好的事情。 (续...)
    【解决方案2】:
    • 根据我的经验,只调用 EndInvoke 而不等待或先被回调很少有用
    • 仅提供回调有时是不够的,因为您的客户可能希望同时等待多个操作(WaitAny、WaitAll)
    • 我从来没有对 IsCompleted 进行过轮询,确实很糟糕!因此,您可以保存 IsCompleted 的实现,但它非常简单,似乎不值得让您的客户感到惊讶。

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

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

    【讨论】:

    • WaitAny 和 WaitAll 只需要一个 WaitHandle,不是吗?
    • 因为我正在考虑 WaitHandle 和 IsCompleted 可用但 AsyncState 不可用的情况。编辑问题以澄清这一点。
    • 我可能会遗漏一些东西,但支持 AsyncState 不是很简单,而且也很便宜吗?
    • 顺便说一句,您似乎正在实现异步委托已经提供的功能,请参阅相关问题?
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2013-01-06
    • 2019-06-17
    • 2015-04-19
    • 2011-07-31
    • 1970-01-01
    • 1970-01-01
    • 2016-06-21
    相关资源
    最近更新 更多