I've reused the example producer consumer queue from the C# in a Nutshell book of Albahari (http://www.albahari.com/threading/part5.aspx#_BlockingCollectionT) and a colleague remarked: "Why isn't the Dispose called on the BlockingCollection in the Dispose of the collection?"
I couldn't find an answer and the only reason I can come up with is that execution of the remaining workload of the queue wouldn't be processed. However, when I'm disposing the queue, why wouldn't it stop processing?
Besides the "Why you shouldn't Dispose the BlockingCollection?" I've got also a second question "Does it harm if you don't dispose a BlockingCollection?". I suppose when you are spawning/disposing a lot of producer consumer queues it gives problems (not that I want that but just for the cause of knowing).
According to What does BlockingCollection.Dispose actually do? BlockingCollection contains two wait handles (obviously) so not calling Dispose will give you some problems. Thanks ken2k for pointing this out.
The code I'm talking about:
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.
}
}
Call Dispose when you are finished using the BlockingCollection<T>. The Dispose method leaves the BlockingCollection<T> in an unusable state. After calling Dispose, you must release all references to the BlockingCollection<T> so the garbage collector can reclaim the memory that the BlockingCollection<T> was occupying.
The Dispose method leaves the BlockingCollection<T> in an unusable state. After calling Dispose, you must release all references to the BlockingCollection<T> so the garbage collector can reclaim the memory that the BlockingCollection<T> was occupying. For more information, see Cleaning Up Unmanaged Resources and Implementing a Dispose Method.
The default collection type for BlockingCollection<T> is ConcurrentQueue<T>. The following code example shows how to create a BlockingCollection<T> of strings that has a capacity of 1000 and uses a ConcurrentBag<T>: For more information, see How to: Add Bounding and Blocking Functionality to a Collection.
The Dispose method is not thread-safe. Call Dispose when you are finished using the BlockingCollection<T>. The Dispose method leaves the BlockingCollection<T> in an unusable state.
Because that would be a bug. The collection cannot be disposed until all the consumer threads have exited. If that's not interlocked then those threads would bomb with an exception. The class does not in any way have awareness of what consumer threads might be pulling from the collection so it cannot reasonably know when it is safe to dispose. All it can do is prevent any more objects from being added by the producer, that's reasonable.
This is a common problem with threads, safely disposing requires knowing when the thread is complete. Which often defeats the point of using threads in the first place, you don't want to wait until a thread ends. This is most visible in the Thread class itself, it consumes five native operating system handles but doesn't have a Dispose() method. They need to be released by the finalizer. Same here.
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