【问题标题】:How can I subclass WaitHandle?我如何继承 WaitHandle?
【发布时间】:2011-12-02 13:24:02
【问题描述】:

有可能(以一种有意义的方式)吗?

例如,假设我想实现一个等待队列,如下所示:

public class WaitableQueue : WaitHandle {
    public WaitableQueue() {
      q = new Queue();
    }

    public void Enqueue(Object Arg) {
      lock(this) {
        q.Enqueue(Arg);
        // set the waithandle here (how?)
      }
    }

    public Type Dequeue() {
      lock(this) {
        if(q.Count == 1)
        // reset the waithandle here (how?)
        return q.Dequeue();
      }
    }

    Queue q;
  }

【问题讨论】:

  • 如果可能的话,您想如何使用它?
  • 不。我给出的例子恰好与一个关于队列的实际问题相吻合。
  • @the_ajp:我完全看不出这个问题与这个问题有什么关系。

标签: c# multithreading


【解决方案1】:

要记住的重要一点是您必须设置SafeWaitHandle 属性。

来自 MSDN:

从 WaitHandle 派生时,请使用 SafeWaitHandle 属性 存储您的本机句柄操作系统句柄。你不需要 覆盖受保护的 Dispose 方法,除非您使用额外的 非托管资源。

我会这样做。

public class QueueWaitHandle<T> : WaitHandle
{
    private Queue<T> queue = new Queue<T>();
    private ManualResetEvent signal = new ManualResetEvent(false);

    public QueueWaitHandle()
    {
        base.SafeWaitHandle = signal.SafeWaitHandle;
    }

    public void Enqueue(T item)
    {
        lock (queue)
        {
            queue.Enqueue(item);
            signal.Set();
        }
    }

    public T Dequeue()
    {
        lock (queue)
        {
            T item = queue.Dequeue();
            if (queue.Count == 0)
            {
                signal.Reset();
            }
            return item;
        }
    }
}

以这种方式完成时,Enqueue 的行为类似于 Set 方法,Dequeue 的行为类似于 Reset 方法。所以基本上这就像一个计数事件,其中非零信号和零信号。在这种情况下,队列正在计数,它恰好也保存了数据。

我知道您通常询问的是关于子类化WaitHandle,但这个特定的数据结构不仅仅是一个练习。在某些情况下它可能很有用。但是,我不会将其称为可等待队列,因为这意味着,至少对我而言,当队列为空时,Dequeue 操作将阻塞。显然,在这个特定的实现中不会发生这种情况。

【讨论】:

  • 你总是可以有 Dequeue(bool Blocking) 但无论如何这就是我正在寻找的解决方案。我本来希望保护 Set 和 Reset 或受保护的 ctor 有一个参数,如 WaitHandle(SafeWaitHandle Handle)。我找不到那些,这就是让我失望的原因(遗憾的是我也错过了你引用的 MSDN 位)。我的问题确实很笼统,​​但它确实来自现实生活场景,您的解决方案将得到很好的利用。谢谢!
【解决方案2】:

EventWaitHandle 类有一个 Set 和 Reset 方法,因此您可以从该方法继承而不是 WaitHandle。

但是,对于提供的示例,我认为这不是一个好的实现,因为 SafeWaitableQueue 类现在有两个不同的角色:队列和等待句柄。

但是,如果您有其他想法需要实现自己的等待句柄,我建议您尝试从 EventWaitHandle 继承。

这不是一个很好的解决方案,因为 Set 和 Reset 方法是公开的,这意味着 SafeWaitableQueue 类的使用者也可以调用 Set 和 Reset,这可能会导致一些明显的un安全行为。

【讨论】:

  • 我认为 EventWaitHandle 会像 AutoResetEvent 和 ManualResetEvent 一样被密封。正如您正确指出的那样,曝光问题是一个问题。
【解决方案3】:

我会创建一个聚合 Queue 和 WaitHandle 的类,所以:

public class WaitableQueue<T>
{
    private Queue<T> _queue;
    private WaitHandle _waitHandle;

    public WaitableQueue()
    {
        _queue = new Queue<T>();
        _waitHandle = new WaitHandle();
    }

    public void Enqueue(T Arg) {
      lock(this) {
        _queue.Enqueue(Arg);
        _waitHandle.Set();
      }
    }
    ...
}

【讨论】:

  • 也许 _waitHandle 应该作为只读公共属性公开和/或应该有一个构造函数重载,它接受 WaitHandle 作为参数。这样,您可以使用此 WaitableQueue 类,如果您需要执行 WaitHandle.WaitAny(q1,q1,...) ,那么您仍然可以这样做,因为您可以访问队列正在使用的 WaitHandle。
  • 如果您将 _waitHandle 公开为公共 get 属性,您将能够做到这一点
  • @schwrz 但是你可以调用 WaitableQueue.WaitAny(q1, q2) 只要​​你提供一种方法来做到这一点。这具有封装等待句柄的优点。但是,如果您的意图是让您的队列与其他等待句柄混合,那么您的方法应该可行,
  • 属性解决方案非常适合我给出的示例,过去我什至在现实生活中使用过它,但我真正想知道的是:你能从 WaitHandle 派生吗? (见问题标题)
  • @tcarvin 我从没想过要实现 WaitAny。无论如何,我的意图确实是拥有一个可以与其他手柄混合的灵活结构,但感谢您说明应该是一个明显的替代方案。
【解决方案4】:

我认为这不是很好地使用继承。你的对象一个队列并且使用一个等待句柄来提供线程安全,所以你不应该从WaitHandle派生。

【讨论】:

  • 我并不是特别想实现一个等待队列。这只是一个例子。我在问 WaitHandle 是否可以以有意义的方式派生(请参阅我的原始帖子)
  • 它是一个抽象基类,因此它以一种有意义的方式派生自 .NET 框架中的所有具体同步类(Mutex、Semaphore 等)。我相信您可以使用它来开发另一种 .NET 不支持开箱即用的同步原语。
  • 你会怎么做呢?在我看来,使用 .NET 原语无法完成,因为您无法控制继承的句柄。
  • 小心这个,不能让 GetEnumerator() 线程安全。因此不应继承 Queue
  • 我知道。我的示例没有继承 Queue。
猜你喜欢
  • 2013-08-02
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-09-17
  • 1970-01-01
  • 2010-12-22
  • 2010-12-08
  • 1970-01-01
相关资源
最近更新 更多