0

I have the below codes in C# for consumer and producer using AutoResetEvent, but they do not work in case having multiple producer and one consumer. The problem is that the consumer can not consume all the items in the queue. When I debug, I notice the consumer can only remove one item and then it returns false and can not remove anymore. Seems the problem is in AutoResetEvent, but I can not figure out what is wrong.

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;
}
Hasan Emrah Süngü
  • 3,488
  • 1
  • 15
  • 33
user3033921
  • 189
  • 1
  • 8
  • 21

2 Answers2

2

The problem with using an AutoResetEvent like this is that you may call Set() twice or more but WaitOne() only once. Calling Set() on an ARE that is already signaled will always fail, the item gets stuck in the queue. A standard threading race bug. Looks like you could fix it by emptying the entire queue in the consumer. Not a real fix, the producer can still race ahead of the consumer, you merely lowered the odds to the once-a-month undebuggable stage.

ARE cannot do this, it cannot count. Use a Semaphore/Slim instead, it was made to count in a thread-safe way. Or use a ConcurrentQueue, a class added to solve exactly this kind of programming problem.

Hans Passant
  • 922,412
  • 146
  • 1,693
  • 2,536
1

By using AutoResetEvent, you have designed the program in such a way that only one consumer can consume an item at a time.

If you want to stick on with the similar design, you can instead use ManualResetEvent, Reset the event when any of the consumer thread finds that there are no items to be consumed, and Set the event when the producer thread knows that there is atleast one item to be consumed.

You can find an alternate design with Monitor class here

You can also can make use of Blocking collection if you are using .NET 4.0 or higher

zumalifeguard
  • 8,648
  • 5
  • 43
  • 56
Arctic
  • 807
  • 10
  • 22