kkocabiyik
kkocabiyik

Reputation: 4406

Concurrently Dequeueing an item

Assume we have two threads working with a method that execute below:

while(true){

    if(Queue.Count() <= 0){
        wait();
    }

    object anObject = Queue.Dequeue();
}

Now the problem occurs when Queue has one element init, Thread 1 is about to execute Queue.Count line, Thread 2 about is on Queue.Dequeue() and execution priority is on Thread 1.

As this situation occurs, Thread 1 will throw an exception because, Queue.Count() will return 1 and it will try to dequeue an object from an empty queue. How can I handle this? What is the best solution if I want to dequeue safely? Should I use syncronized or lock something?

Best regards, Kemal

Upvotes: 0

Views: 605

Answers (4)

user854301
user854301

Reputation: 5493

You can use thread safe queue ConcurrentQueue.

or if you don't want to use it

    while (true)
    {
        Monitor.Enter(lockObj);
        try
        {
            if (Queue.Count <= 0)
            {
                Monitor.Wait(lockObj);
            }

            object anObject = Queue.Dequeue();
        }
        finally
        {
            Monitor.Exit(lockObj);
        }
    }

or if using lock

    while (true)
    {
        lock(lockObj)
        {
            if (Queue.Count <= 0)
            {
                Monitor.Wait(lockObj);
            }

            object anObject = Queue.Dequeue();
        }
    }

Upvotes: 1

Otiel
Otiel

Reputation: 18743

Lock the queue before accessing it.

lock (Queue) {
    // blah blah
}

EDIT

while(true){
    lock (Queue) {
        if (Queue.Count() > 0) {
            // Dequeue only if there is still something in the queue
            object anObject = Queue.Dequeue();
        }
    }     
}

Upvotes: 0

serge.karalenka
serge.karalenka

Reputation: 990

Try this pattern:

Producer

public void Produce(object o)
{
  lock (_queueLock)
  {
    _queue.Enqueue(o);
    Monitor.Pulse(_queueLock);
  }
}

Consumer

public object Consume()
{
  lock (_queueLock)
  {
    while (_queue.Count==0)
    {
      Monitor.Wait(_queueLock);
    }
    return _queue.Dequeue();
  }
}

Upvotes: 0

Chris Shain
Chris Shain

Reputation: 51339

The best solution, assuming you are using .NET 4.0 or higher and really need a queue, is to switch to using ConcurrentQueue and it's TryDequeue method. ConcurrentQueue is thread safe.

That said, it looks from your code snippet like what you are really looking for is a thread safe producer/consumer queue. In that case, use the BlockingCollection class and it's Take method:

while(true){
    // This will block until an item becomes available to take.
    // It is also thread safe, and can be called by multiple 
    // threads simultaneously. When an item is added, only one
    // waiting thread will Take() it
    object anObject = myBlockingCollection.Take();

    // do something with anObject
}

Upvotes: 2

Related Questions