对于使用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;
}
答案 0 :(得分:1)
通过使用AutoResetEvent,您可以设计程序,使消费者一次只能使用一个项目。
如果你想坚持使用类似的设计,你可以在任何消费者线程发现没有要消费的物品时使用ManualResetEvent,重置该事件,并且当生产者线程知道至少有一个要消费的项目时,设置事件。
您可以使用Monitor类here
找到备用设计如果您使用的是.NET 4.0或更高版本,也可以使用Blocking collection
答案 1 :(得分:1)
使用像这样的AutoResetEvent的问题是你可以调用Set()两次或更多但WaitOne()只调用一次。在已经发出信号的ARE上调用Set()将始终失败,该项目将卡在队列中。标准的线程竞争错误。看起来你可以通过清空消费者中的整个队列来修复它。不是真正的解决方案,制作人仍然可以在消费者面前比赛,你只是降低了每月一次的不可判断阶段的几率。
不能做到这一点,它不能算数。使用Semaphore / Slim代替它,它是以线程安全的方式计数的。或者使用ConcurrentQueue,这个类被添加来解决这种编程问题。