What are the differences between ConcurrentQueue
and BlockingCollection
in .Net?
Why BlockingCollection
is best for producer-consumer operation when it can be done through ConcurrentQueue
? Do I have to improve anything in the following code?
MessageSlotMachineGameStartOrAndStatusUpdate msg; while (!aCancellationToken.IsCancellationRequested) { try { this.isStillConsumingMsg = true; Boolean takeResult = this.msgQueue.TryTake(out msg, this.msgConsumeTimeOut, aCancellationToken); if (takeResult) { if (msg != null) { this.ProcessMessage(msg); } } else { break; } } catch (OperationCanceledException err) { EngineManager.AddExceptionLog(err, "Signal Operation Canceled"); } catch (Exception err) { EngineManager.AddExceptionLog(err, "Signal exception"); } finally { this.isStillConsumingMsg = false; } }
BlockingCollection<T> is a thread-safe collection class that provides the following features: An implementation of the Producer-Consumer pattern. Concurrent adding and taking of items from multiple threads. Optional maximum capacity. Insertion and removal operations that block when collection is empty or full.
Note that both ConcurrentStack and ConcurrentQueue classes are thread safe and they can manage locking and synchronization issues internally. You can also convert the concurrent queue instance to an array by making a call to the ToArray() method.
Perhaps MSDN documentation has been updated since this question but it now clearly states that BlockingCollection will default to FIFO unless otherwise directed.
Unlike a LinkedBlockingQueue, a ConcurrentLinkedQueue is a non-blocking queue. Thus, it does not block a thread once the queue is empty. Instead, it returns null.
BlockingCollection
has a Take
method that would block the consumer if there is nothing to take, and wait for a producer side to provide an item. ConcurrentQueue
lacks such method - if it is empty, the consumer would need to handle the wait, and the producer would need to provide a non-empty notification.
The BlockingCollection is a wrapper for concurrent collections, and the default wrapped collection IS a ConcurrentQueue ;-)
From Microsoft BlockingCollection:
When you create a BlockingCollection object, you can specify not only the bounded capacity but also the type of collection to use. For example, you could specify a ConcurrentQueue object for first in, first out (FIFO) behavior, or a ConcurrentStack object for last in, first out (LIFO) behavior. You can use any collection class that implements the IProducerConsumerCollection interface. The default collection type for BlockingCollection is ConcurrentQueue.
The BlockingCollection has a Take() blocking method (hence the name), but it also has a very interesting GetConsumingEnumerable() method which allows you to loop indefinitely : the code will enter the loop inside code only when something is added to collection. See albahari.com excellent online ebook about Threading.
Here is the code sample from this website:
public class PCQueue : IDisposable { BlockingCollection<Action> _taskQ = new BlockingCollection<Action>(); public PCQueue (int workerCount) { // Create and start a separate Task for each consumer: for (int i = 0; i < workerCount; i++) Task.Factory.StartNew (Consume); } public void Dispose() { _taskQ.CompleteAdding(); } public void EnqueueTask (Action action) { _taskQ.Add (action); } void Consume() { // This sequence that we’re enumerating will block when no elements // are available and will end when CompleteAdding is called. foreach (Action action in _taskQ.GetConsumingEnumerable()) action(); // Perform task. } }
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With