Queue <T> .Dequeue returns null
I have a scenario in which
multiple threads push data in line
only one thread processes data using the code below
code -
while ( Continue ) { while ( queue.Count > 0 ) { MyObj o = queue.Dequeue(); someProcess(o); } myAutoResetEvent.WaitOne(); } But sometimes queue.Dequeue () returns null in the above script. What gives?
You need to read this blog post .
In addition, there is a very minimal "channel" skeleton for communication between threads:
public class Channel<T> { private readonly Queue<T> _queue = new Queue<T>(); public void Enqueue(T item) { lock (_queue) { _queue.Enqueue(item); if (_queue.Count == 1) Monitor.PulseAll(_queue); } } public T Dequeue() { lock (_queue) { while (_queue.Count == 0) Monitor.Wait(_queue); return _queue.Dequeue(); } } } You need to synchronize access to the queue. Put lock expressions around all sections of the code that access the queue (both read and write). If you access a queue from multiple threads at the same time, internal structures may be damaged and something may happen.
You speak:
multiple threads push data in line
The Queue<T>.Enqueue not thread safe. This means that the work is performed inside the Enqueue method, which must be synchronized if several threads are calling it. A simple example would be updating the Count property. This is a safe bet that somewhere in the Enqueue method Enqueue is a line that looks something like this:
++count; But, as we all know, this is not an atomic operation. It really looks more like this (in terms of what is actually happening):
int newCount = count + 1; count = newCount; So let's say that Count is currently 5, and Thread 1 passes by int newCount = count + 1 ... then Thread 1 thinks: "OK, the count is now 5, so I'll do it." But the next operation that is performed is where Thread 2 falls into int newCount = count + 1 and thinks the same as Thread 1 ("the count is now 6"). Thus, two objects have just been added to the queue, but the count is only 5 to 6.
This is just a very simple example of how a non-thread-safe method, such as Queue<T>.Enqueue , can be confused when access is not synchronized. It does not specifically indicate what is happening in your question; my intention is simply to indicate that what you are doing is not thread safe and will lead to unexpected behavior .
Guffa is right, having multiple threads, reading and writing to the queue will cause problems because Queue <T> is not a reliable thread.
If you are using .NET 4, use the ConcurrentQueue <T> class, which is thread safe. If you are not on .NET 3 or earlier, you can either do your own locking, as Guff pointed out, or use a third-party library.
Make sure nothing is pressed null into the queue. null allowed as values ββin the queue. Also, according to this document , only Queue<T> static members are thread safe, so beware of reading and writing to streams.
Why don't you solve the problem this way?
while (IsRunning) { do { MyObj myObj = queue.Dequeue(); if (myObj != null) { DoSomethingWith(myObj); } } while (myObj != null); myAutoResetEvent.WaitOne(); } Update
Well, after reading the Earwickers comment and all the other answers, you're fine, and I'm just wrong. Therefore , please do not use the code above in multi-threaded contexts .
If you are using a non-core queue (not what I recommend using it), you can use the Queue.Synchronized method to get a thread-safe shell:
Queue queue = Queue.Synchronized(new Queue()); Otherwise, you must take care to lock yourself up, as others suggest.