BlockingCollection<T>
Class
Definition
Provides blocking and bounding capabilities for thread-safe collections that implement IProducerConsumerCollection<T>.
[System.Runtime.InteropServices.ComVisible(false)]
public class BlockingCollection<T> : IDisposable, System.Collections.Generic.IEnumerable<T>, System.Collections.Generic.IReadOnlyCollection<T>, System.Collections.ICollection
- T
The type of elements in the collection.
- Inheritance
-
BlockingCollection<T>
- Attributes
- Implements
Inherited Members
System.Object
Examples
The following example shows how to add and take items concurrently from a blocking collection:
using System;
using System.Collections;
using System.Collections.Generic;
using System.Collections.Concurrent;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
class BlockingCollectionDemo
{
static void Main()
{
AddTakeDemo.BC_AddTakeCompleteAdding();
TryTakeDemo.BC_TryTake();
FromToAnyDemo.BC_FromToAny();
ConsumingEnumerableDemo.BC_GetConsumingEnumerable();
Console.WriteLine("Press any key to exit.");
Console.ReadKey();
}
}
class AddTakeDemo
{
// Demonstrates:
// BlockingCollection<T>.Add()
// BlockingCollection<T>.Take()
// BlockingCollection<T>.CompleteAdding()
public static void BC_AddTakeCompleteAdding()
{
using (BlockingCollection<int> bc = new BlockingCollection<int>())
{
// Spin up a Task to populate the BlockingCollection
using (Task t1 = Task.Factory.StartNew(() =>
{
bc.Add(1);
bc.Add(2);
bc.Add(3);
bc.CompleteAdding();
}))
{
// Spin up a Task to consume the BlockingCollection
using (Task t2 = Task.Factory.StartNew(() =>
{
try
{
// Consume consume the BlockingCollection
while (true) Console.WriteLine(bc.Take());
}
catch (InvalidOperationException)
{
// An InvalidOperationException means that Take() was called on a completed collection
Console.WriteLine("That's All!");
}
}))
Task.WaitAll(t1, t2);
}
}
}
}
class TryTakeDemo
{
// Demonstrates:
// BlockingCollection<T>.Add()
// BlockingCollection<T>.CompleteAdding()
// BlockingCollection<T>.TryTake()
// BlockingCollection<T>.IsCompleted
public static void BC_TryTake()
{
// Construct and fill our BlockingCollection
using (BlockingCollection<int> bc = new BlockingCollection<int>())
{
int NUMITEMS = 10000;
for (int i = 0; i < NUMITEMS; i++) bc.Add(i);
bc.CompleteAdding();
int outerSum = 0;
// Delegate for consuming the BlockingCollection and adding up all items
Action action = () =>
{
int localItem;
int localSum = 0;
while (bc.TryTake(out localItem)) localSum += localItem;
Interlocked.Add(ref outerSum, localSum);
};
// Launch three parallel actions to consume the BlockingCollection
Parallel.Invoke(action, action, action);
Console.WriteLine("Sum[0..{0}) = {1}, should be {2}", NUMITEMS, outerSum, ((NUMITEMS*(NUMITEMS - 1))/2));
Console.WriteLine("bc.IsCompleted = {0} (should be true)", bc.IsCompleted);
}
}
}
class FromToAnyDemo
{
// Demonstrates:
// Bounded BlockingCollection<T>
// BlockingCollection<T>.TryAddToAny()
// BlockingCollection<T>.TryTakeFromAny()
public static void BC_FromToAny()
{
BlockingCollection<int>[] bcs = new BlockingCollection<int>[2];
bcs[0] = new BlockingCollection<int>(5); // collection bounded to 5 items
bcs[1] = new BlockingCollection<int>(5); // collection bounded to 5 items
// Should be able to add 10 items w/o blocking
int numFailures = 0;
for (int i = 0; i < 10; i++)
{
if (BlockingCollection<int>.TryAddToAny(bcs, i) == -1) numFailures++;
}
Console.WriteLine("TryAddToAny: {0} failures (should be 0)", numFailures);
// Should be able to retrieve 10 items
int numItems = 0;
int item;
while (BlockingCollection<int>.TryTakeFromAny(bcs, out item) != -1) numItems++;
Console.WriteLine("TryTakeFromAny: retrieved {0} items (should be 10)", numItems);
}
}
class ConsumingEnumerableDemo
{
// Demonstrates:
// BlockingCollection<T>.Add()
// BlockingCollection<T>.CompleteAdding()
// BlockingCollection<T>.GetConsumingEnumerable()
public static void BC_GetConsumingEnumerable()
{
using (BlockingCollection<int> bc = new BlockingCollection<int>())
{
// Kick off a producer task
Task.Factory.StartNew(() =>
{
for (int i = 0; i < 10; i++)
{
bc.Add(i);
Thread.Sleep(100); // sleep 100 ms between adds
}
// Need to do this to keep foreach below from hanging
bc.CompleteAdding();
});
// Now consume the blocking collection with foreach.
// Use bc.GetConsumingEnumerable() instead of just bc because the
// former will block waiting for completion and the latter will
// simply take a snapshot of the current state of the underlying collection.
foreach (var item in bc.GetConsumingEnumerable())
{
Console.WriteLine(item);
}
}
}
}
Imports System.Threading.Tasks
Imports System.Collections.Concurrent
Imports System.Threading
Class BlockingCollectionDemo
Shared Sub Main()
AddTakeDemo.BC_AddTakeCompleteAdding()
TryTakeDemo.BC_TryTake()
ToAnyDemo.BC_ToAny()
ConsumingEnumerableDemo.BC_GetConsumingEnumerable()
' Keep the console window open in debug mode
Console.WriteLine("Press any key to exit.")
Console.ReadKey()
End Sub
End Class
Class AddTakeDemo
' Demonstrates:
' BlockingCollection<T>.Add()
' BlockingCollection<T>.Take()
' BlockingCollection<T>.CompleteAdding()
Shared Sub BC_AddTakeCompleteAdding()
Using bc As New BlockingCollection(Of Integer)()
' Spin up a Task to populate the BlockingCollection
Using t1 As Task = Task.Factory.StartNew(
Sub()
bc.Add(1)
bc.Add(2)
bc.Add(3)
bc.CompleteAdding()
End Sub)
' Spin up a Task to consume the BlockingCollection
Using t2 As Task = Task.Factory.StartNew(
Sub()
Try
' Consume the BlockingCollection
While True
Console.WriteLine(bc.Take())
End While
Catch generatedExceptionName As InvalidOperationException
' An InvalidOperationException means that Take() was called on a completed collection
Console.WriteLine("That's All!")
End Try
End Sub)
Task.WaitAll(t1, t2)
End Using
End Using
End Using
End Sub
End Class
'Imports System.Collections.Concurrent
' Imports System.Threading
'Imports System.Threading.Tasks
Class TryTakeDemo
' Demonstrates:
' BlockingCollection<T>.Add()
' BlockingCollection<T>.CompleteAdding()
' BlockingCollection<T>.TryTake()
' BlockingCollection<T>.IsCompleted
Shared Sub BC_TryTake()
' Construct and fill our BlockingCollection
Using bc As New BlockingCollection(Of Integer)()
Dim NUMITEMS As Integer = 10000
For i As Integer = 0 To NUMITEMS - 1
bc.Add(i)
Next
bc.CompleteAdding()
Dim outerSum As Integer = 0
' Delegate for consuming the BlockingCollection and adding up all items
Dim action As Action =
Sub()
Dim localItem As Integer
Dim localSum As Integer = 0
While bc.TryTake(localItem)
localSum += localItem
End While
Interlocked.Add(outerSum, localSum)
End Sub
' Launch three parallel actions to consume the BlockingCollection
Parallel.Invoke(action, action, action)
Console.WriteLine("Sum[0..{0}) = {1}, should be {2}", NUMITEMS, outerSum, ((NUMITEMS * (NUMITEMS - 1)) / 2))
Console.WriteLine("bc.IsCompleted = {0} (should be true)", bc.IsCompleted)
End Using
End Sub
End Class
'Imports System.Threading.Tasks
'Imports System.Collections.Concurrent
' Demonstrates:
' Bounded BlockingCollection<T>
' BlockingCollection<T>.TryAddToAny()
' BlockingCollection<T>.TryTakeFromAny()
Class ToAnyDemo
Shared Sub BC_ToAny()
Dim bcs As BlockingCollection(Of Integer)() = New BlockingCollection(Of Integer)(1) {}
bcs(0) = New BlockingCollection(Of Integer)(5)
' collection bounded to 5 items
bcs(1) = New BlockingCollection(Of Integer)(5)
' collection bounded to 5 items
' Should be able to add 10 items w/o blocking
Dim numFailures As Integer = 0
For i As Integer = 0 To 9
If BlockingCollection(Of Integer).TryAddToAny(bcs, i) = -1 Then
numFailures += 1
End If
Next
Console.WriteLine("TryAddToAny: {0} failures (should be 0)", numFailures)
' Should be able to retrieve 10 items
Dim numItems As Integer = 0
Dim item As Integer
While BlockingCollection(Of Integer).TryTakeFromAny(bcs, item) <> -1
numItems += 1
End While
Console.WriteLine("TryTakeFromAny: retrieved {0} items (should be 10)", numItems)
End Sub
End Class
'Imports System.Threading.Tasks
'Imports System.Collections.Concurrent
' Demonstrates:
' BlockingCollection<T>.Add()
' BlockingCollection<T>.CompleteAdding()
' BlockingCollection<T>.GetConsumingEnumerable()
Class ConsumingEnumerableDemo
Shared Sub BC_GetConsumingEnumerable()
Using bc As New BlockingCollection(Of Integer)()
' Kick off a producer task
Task.Factory.StartNew(
Sub()
For i As Integer = 0 To 9
bc.Add(i)
' sleep 100 ms between adds
Thread.Sleep(100)
Next
' Need to do this to keep foreach below from hanging
bc.CompleteAdding()
End Sub)
' Now consume the blocking collection with foreach.
' Use bc.GetConsumingEnumerable() instead of just bc because the
' former will block waiting for completion and the latter will
' simply take a snapshot of the current state of the underlying collection.
For Each item In bc.GetConsumingEnumerable()
Console.WriteLine(item)
Next
End Using
End Sub
End Class
Remarks
BlockingCollection<T> is a thread-safe collection class that provides the following:
An implementation of the producer/consumer pattern; BlockingCollection<T> is a wrapper for the IProducerConsumerCollection<T> interface.
Concurrent addition and removal of items from multiple threads with the Add and Take methods.
A bounded collection that blocks Add and Take operations when the collection is full or empty.
Cancellation of Add or Take operations by using a CancellationToken object in the TryAdd or TryTake method.
Important
This type implements the IDisposable interface. When you have finished using the type, you should dispose of it either directly or indirectly. To dispose of the type directly, call its Dispose method in a try/catch block. To dispose of it indirectly, use a language construct such as using (in C#) or Using (in Visual Basic). For more information, see the "Using an Object that Implements IDisposable" section in the IDisposable interface topic. Also, note that the Dispose() method is not thread-safe. All other public and protected members of BlockingCollection<T> are thread-safe and may be used concurrently from multiple threads.
IProducerConsumerCollection<T> represents a collection that allows for thread-safe adding and removal of data. BlockingCollection<T> is used as a wrapper for an IProducerConsumerCollection<T> instance, and allows removal attempts from the collection to block until data is available to be removed. Similarly, you can create a BlockingCollection<T> to enforce an upper bound on the number of data elements allowed in the IProducerConsumerCollection<T>; addition attempts to the collection may then block until space is available to store the added items. In this manner, BlockingCollection<T> is similar to a traditional blocking queue data structure, except that the underlying data storage mechanism is abstracted away as an IProducerConsumerCollection<T>.
BlockingCollection<T> supports bounding and blocking. Bounding means that you can set the maximum capacity of the collection. Bounding is important in certain scenarios because it enables you to control the maximum size of the collection in memory, and it prevents the producing threads from moving too far ahead of the consuming threads.Multiple threads or tasks can add items to the collection concurrently, and if the collection reaches its specified maximum capacity, the producing threads will block until an item is removed. Multiple consumers can remove items concurrently, and if the collection becomes empty, the consuming threads will block until a producer adds an item. A producing thread can call the CompleteAdding method to indicate that no more items will be added. Consumers monitor the IsCompleted property to know when the collection is empty and no more items will be added.
Add and Take operations are typically performed in a loop. You can cancel a loop by passing in a CancellationToken object to the TryAdd or TryTake method, and then checking the value of the token's IsCancellationRequested property on each iteration. If the value is true, it is up to you to respond the cancellation request by cleaning up any resources and exiting the loop.
When you create a BlockingCollection<T> object, you can specify not only the bounded capacity but also the type of collection to use. For example, you could specify a ConcurrentQueue<T> object for first in, first out (FIFO) behavior, or a ConcurrentStack<T> object for last in, first out (LIFO) behavior. You can use any collection class that implements the IProducerConsumerCollection<T> interface. The default collection type for BlockingCollection<T> is ConcurrentQueue<T>.
Do not modify the underlying collection directly. Use BlockingCollection<T> methods to add or remove elements. The BlockingCollection<T> object can become corrupted if you change the underlying collection directly.
Constructors
| BlockingCollection<T>() |
Initializes a new instance of the BlockingCollection<T> class without an upper-bound. |
| BlockingCollection<T>(IProducerConsumerCollection<T>) |
Initializes a new instance of the BlockingCollection<T> class without an upper-bound and using the provided IProducerConsumerCollection<T> as its underlying data store. |
| BlockingCollection<T>(Int32) |
Initializes a new instance of the BlockingCollection<T> class with the specified upper-bound. |
| BlockingCollection<T>(IProducerConsumerCollection<T>, Int32) |
Initializes a new instance of the BlockingCollection<T> class with the specified upper-bound and using the provided IProducerConsumerCollection<T> as its underlying data store. |
Properties
| BoundedCapacity |
Gets the bounded capacity of this BlockingCollection<T> instance. |
| Count |
Gets the number of items contained in the BlockingCollection<T>. |
| IsAddingCompleted |
Gets whether this BlockingCollection<T> has been marked as complete for adding. |
| IsCompleted |
Gets whether this BlockingCollection<T> has been marked as complete for adding and is empty. |
Methods
| Add(T) |
Adds the item to the BlockingCollection<T>. |
| Add(T, CancellationToken) |
Adds the item to the BlockingCollection<T>. |
| AddToAny(BlockingCollection<T>[], T) |
Adds the specified item to any one of the specified BlockingCollection<T> instances. |
| AddToAny(BlockingCollection<T>[], T, CancellationToken) |
Adds the specified item to any one of the specified BlockingCollection<T> instances. |
| CompleteAdding() |
Marks the BlockingCollection<T> instances as not accepting any more additions. |
| CopyTo(T[], Int32) |
Copies all of the items in the BlockingCollection<T> instance to a compatible one-dimensional array, starting at the specified index of the target array. |
| Dispose() |
Releases all resources used by the current instance of the BlockingCollection<T> class. |
| Dispose(Boolean) |
Releases resources used by the BlockingCollection<T> instance. |
| GetConsumingEnumerable(CancellationToken) |
Provides a consuming IEnumerable<T> for items in the collection. |
| GetConsumingEnumerable() |
Provides a consuming IEnumerator<T> for items in the collection. |
| Take() |
Removes an item from the BlockingCollection<T>. |
| Take(CancellationToken) |
Removes an item from the BlockingCollection<T>. |
| TakeFromAny(BlockingCollection<T>[], T) |
Takes an item from any one of the specified BlockingCollection<T> instances. |
| TakeFromAny(BlockingCollection<T>[], T, CancellationToken) |
Takes an item from any one of the specified BlockingCollection<T> instances while observing the specified cancellation token. |
| ToArray() |
Copies the items from the BlockingCollection<T> instance into a new array. |
| TryAdd(T, Int32) |
Tries to add the specified item to the BlockingCollection<T> within the specified time period. |
| TryAdd(T, Int32, CancellationToken) |
Tries to add the specified item to the BlockingCollection<T> within the specified time period, while observing a cancellation token. |
| TryAdd(T) |
Tries to add the specified item to the BlockingCollection<T>. |
| TryAdd(T, TimeSpan) |
Tries to add the specified item to the BlockingCollection<T>. |
| TryAddToAny(BlockingCollection<T>[], T, Int32, CancellationToken) |
Tries to add the specified item to any one of the specified BlockingCollection<T> instances. |
| TryAddToAny(BlockingCollection<T>[], T, TimeSpan) |
Tries to add the specified item to any one of the specified BlockingCollection<T> instances while observing the specified cancellation token. |
| TryAddToAny(BlockingCollection<T>[], T, Int32) |
Tries to add the specified item to any one of the specified BlockingCollection<T> instances. |
| TryAddToAny(BlockingCollection<T>[], T) |
Tries to add the specified item to any one of the specified BlockingCollection<T> instances. |
| TryTake(T) |
Tries to remove an item from the BlockingCollection<T>. |
| TryTake(T, TimeSpan) |
Tries to remove an item from the BlockingCollection<T> in the specified time period. |
| TryTake(T, Int32, CancellationToken) |
Tries to remove an item from the BlockingCollection<T> in the specified time period while observing a cancellation token. |
| TryTake(T, Int32) |
Tries to remove an item from the BlockingCollection<T> in the specified time period. |
| TryTakeFromAny(BlockingCollection<T>[], T) |
Tries to remove an item from any one of the specified BlockingCollection<T> instances. |
| TryTakeFromAny(BlockingCollection<T>[], T, Int32) |
Tries to remove an item from any one of the specified BlockingCollection<T> instances. |
| TryTakeFromAny(BlockingCollection<T>[], T, Int32, CancellationToken) |
Tries to remove an item from any one of the specified BlockingCollection<T> instances. |
| TryTakeFromAny(BlockingCollection<T>[], T, TimeSpan) |
Tries to remove an item from any one of the specified BlockingCollection<T> instances. |
Explicit Interface Implementations
| IEnumerable<T>.GetEnumerator() |
Provides an IEnumerator<T> for items in the collection. |
| ICollection.CopyTo(Array, Int32) |
Copies all of the items in the BlockingCollection<T> instance to a compatible one-dimensional array, starting at the specified index of the target array. |
| ICollection.IsSynchronized |
Gets a value indicating whether access to the ICollection is synchronized. |
| ICollection.SyncRoot |
Gets an object that can be used to synchronize access to the ICollection. This property is not supported. |
| IEnumerable.GetEnumerator() |
Provides an IEnumerator for items in the collection. |
Extension Methods
Thread Safety
The Dispose method is not thread-safe. All other public and protected members of BlockingCollection<T> are thread-safe and may be used concurrently from multiple threads.