使用AutoResetEvent的消费者/生产者

时间:2016-05-29 10:11:31

标签: c# queue producer-consumer autoresetevent

对于使用AutoResetEvent的消费者和生产者,我在C#中有以下代码,但是如果有多个生产者和一个消费者,它们不起作用。问题是消费者不能消耗队列中的所有项目。当我调试时,我注意到消费者只能删除一个项目,然后它返回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;
        }

2 个答案:

答案 0 :(得分:1)

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

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

您可以使用Monitor类here

找到备用设计

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

答案 1 :(得分:1)

使用像这样的AutoResetEvent的问题是你可以调用Set()两次或更多但WaitOne()只调用一次。在已经发出信号的ARE上调用Set()将始终失败,该项目将卡在队列中。标准的线程竞争错误。看起来你可以通过清空消费者中的整个队列来修复它。不是真正的解决方案,制作人仍然可以在消费者面前比赛,你只是降低了每月一次的不可判断阶段的几率。

不能做到这一点,它不能算数。使用Semaphore / Slim代替它,它是以线程安全的方式计数的。或者使用ConcurrentQueue,这个类被添加来解决这种编程问题。