【问题标题】:Consumer/Producer with AutoResetEvent使用 AutoResetEvent 的消费者/生产者
【发布时间】:2016-05-29 10:11:30
【问题描述】:

我在 C# 中为使用 AutoResetEvent 的消费者和生产者提供了以下代码,但在有多个生产者和一个消费者的情况下它们不起作用。问题是消费者不能消费队列中的所有项目。当我调试时,我注意到消费者只能删除一项,然后它返回 false 并且不能再删除。似乎问题出在 AutoResetEvent 中,但我不知道出了什么问题。

private AutoResetEvent newItemSignal = new AutoResetEvent(false);
private Queue<Task> iQueue = new Queue<Task>();

public void Enqueue(Task task)
{
    lock (((ICollection)iQueue).SyncRoot)
    {
        iQueue.Enqueue(task);
        newItemSignal.Set();
    }
}



public bool Dequeue(out Task task, int timeout)
{
    if (newItemSignal.WaitOne(timeout, false))
    {
        lock (((ICollection)iQueue).SyncRoot)
        {
            task = iQueue.Dequeue();
        }
        return true;
    }
    task = default(Task);
    return false;
}

【问题讨论】:

  • 如果您使用的是 .NET 4.0 或更高版本,您可能需要使用 BlockingCollection

标签: c# queue producer-consumer autoresetevent


【解决方案1】:

使用这样的 AutoResetEvent 的问题是您可能调用 Set() 两次或更多次,但 WaitOne() 只能调用一次。在已经发出信号的 ARE 上调用 Set() 总是会失败,该项目会卡在队列中。一个标准的线程竞赛错误。看起来你可以通过清空消费者中的整个队列来修复它。不是真正的解决方案,生产者仍然可以领先于消费者,您只是将几率降低到每月一次的不可调试阶段。

ARE 不能这样做,它不能计数。改用 Semaphore/Slim,它以线程安全的方式计数。或者使用 ConcurrentQueue,添加一个类来解决这类编程问题。

【讨论】:

    【解决方案2】:

    通过使用 AutoResetEvent,您将程序设计为一次只有一个消费者可以消费一个项目。

    如果你想坚持类似的设计,你可以改为使用ManualResetEvent,Reset当任何消费者线程发现没有物品可以消费时,当生产者线程知道至少有一项要消费时设置事件。

    您可以找到具有 Monitor 类 here 的替代设计

    如果您使用 .NET 4.0 或更高版本,您也可以使用Blocking collection

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2018-09-25
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多