Question

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.
  }
}
Was it helpful?

Solution

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.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top