liveBook · Manning

BlockingCollection<T>

As explained in C# Concurrency: Asynchronous and multithreaded programming

BlockingCollection<T> is a .NET generic collection type that supports thread-safe production and consumption of items, including blocking consumption until items become available.

BlockingCollection<T> coordinates thread-safe producers and consumers by waiting when no item is available. It fits work queues where background workers process items sequentially or concurrently. section 8.2.2 section 13.2.2

Use the collection through a small set of operations:

  • Add enqueues an item for processing. section 8.2.2
  • GetConsumingEnumerable supports foreach and waits while the queue is empty. section 8.2.2
  • CompleteAdding signals that no more items will arrive, allowing consumers to finish after draining the queue. section 8.2.2
  • Bounded capacity prevents producers from getting too far ahead of consumers. section 13.2.2

The default backing collection is ConcurrentQueue<T>, preserving first-in, first-out order. ConcurrentStack<T> provides last-in, first-out behavior, while ConcurrentBag<T> is specialized for threads that both read and write items. section 13.2.2

Background mail processing

A mail-merging component enqueues personalized messages, starts a processing thread, consumes them with GetConsumingEnumerable, logs send failures, and stops by calling CompleteAdding. Multiple workers can consume the same queue when processing parallelism is useful. section 8.2.2 section 13.2.2

Listing 13.11 BlockingCollection with 10 processing threads
BlockingCollection<int> blockingCollection = new BlockingCollection<int>();
Thread[] workers =  new Thread[10];
for(int i=0; i<workers.Length; i++)                       #1
{
   workers[i] = new Thread(threadNumber =>
   {
      var rng = new Random((int)threadNumber);
      int count = 0;
      foreach (var currentValue in
            blockingCollection.GetConsumingEnumerable())
      {
         Console.WriteLine($"thread {threadNumber} value {currentValue}");
         Thread.Sleep(rng.Next(500));
         count++;
      }
      Console.WriteLine($"thread {threadNumber}, total {count} items");
   });
   workers[i].Start(i);
}
for(int i=0;i<100;i++)                                    #2
{
   blockingCollection.Add(i);
}
blockingCollection.CompleteAdding();                      #3
foreach (var curentThread in workers)                     #4
   curentThread.Join();

A complete pattern starts ten worker threads, consumes values with GetConsumingEnumerable, adds one hundred items, calls CompleteAdding, and waits for workers to finish. section 13.2.2

How C# Concurrency: Asynchronous and multithreaded programming covers it

How deep it goes
Explains operations, backing collection choices, shutdown, capacity limits, and worker implementations, but defers asynchronous alternatives.
Examples
Demonstrates mail merging, one background sender, and ten worker threads processing one hundred queued integers.
What it assumes
Assumes basic threading and collections, while teaching producer-consumer coordination and completion signaling.
What you can do after
Build it from scratch as a bounded or unbounded work queue with orderly completion and one or more consumers.
How it connects
Connects producer-consumer systems with ConcurrentQueue<T>, ConcurrentStack<T>, ConcurrentBag<T>, thread pools, and Channel<T>.

Related concepts

This explainer was generated by Manning's knowledge graph from the text of C# Concurrency: Asynchronous and multithreaded programming. The section links point to the parts of the book it draws on.