BlockingCollection の概要BlockingCollection Overview

BlockingCollection<T> は、次の機能を提供するスレッド セーフなコレクション クラスです。BlockingCollection<T> is a thread-safe collection class that provides the following features:

  • Producer-Consumer パターンの実装。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.

  • ブロックせずに実行されるか、指定された時間が経過するまでブロックする、挿入および削除の "試行" 操作。Insertion and removal "try" operations that do not block or that block up to a specified period of time.

  • IProducerConsumerCollection<T> を実装する任意のコレクション型のカプセル化。Encapsulates any collection type that implements IProducerConsumerCollection<T>

  • キャンセル トークンを使用したキャンセル。Cancellation with cancellation tokens.

  • foreach を使用した 2 種類の列挙 (Visual Basic の場合は For Each)。Two kinds of enumeration with foreach (For Each in Visual Basic):

    1. 読み取り専用の列挙。Read-only enumeration.

    2. 列挙された項目を削除する列挙。Enumeration that removes items as they are enumerated.

境界とブロッキングのサポートBounding and Blocking Support

BlockingCollection<T> はバインドとブロックに対応しています。BlockingCollection<T> supports bounding and blocking. 境界は、コレクションの最大容量を設定できることを意味します。Bounding means you can set the maximum capacity of the collection. 境界を使用すると、メモリ内のコレクションの最大サイズを制御し、producer スレッドが consumer スレッドよりも先に進行しすぎるのを防ぐことができます。これは、特定のシナリオで重要になります。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.

コレクションには、複数のスレッドやタスクが同時に項目を追加できます。コレクションが指定された最大容量に達すると、producer スレッドは項目が削除されるまでブロックします。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. 複数の cosumer が同時に項目を削除できます。コレクションが空になった場合、consumer スレッドは、producer が項目を追加するまでブロックします。Multiple consumers can remove items concurrently, and if the collection becomes empty, the consuming threads will block until a producer adds an item. producer スレッドでは、それ以上項目が追加されないことを示すために、CompleteAdding を呼び出すことができます。A producing thread can call CompleteAdding to indicate that no more items will be added. consumer では、IsCompleted プロパティを監視して、コレクションが空になったときや、それ以上の項目は追加されないことになったときを把握できます。Consumers monitor the IsCompleted property to know when the collection is empty and no more items will be added. 次の例は、容量の上限が 100 に設定された単純な BlockingCollection を示しています。The following example shows a simple BlockingCollection with a bounded capacity of 100. いくつかの外部条件が true である間、producer タスクはコレクションに項目を追加し、CompleteAdding を呼び出します。A producer task adds items to the collection as long as some external condition is true, and then calls CompleteAdding. consumer タスクは、IsCompleted プロパティが true になるまで項目を取得します。The consumer task takes items until the IsCompleted property is true.

// A bounded collection. It can hold no more
// than 100 items at once.
BlockingCollection<Data> dataItems = new BlockingCollection<Data>(100);

// A simple blocking consumer with no cancellation.
Task.Run(() =>
{
    while (!dataItems.IsCompleted)
    {

        Data data = null;
        // Blocks if dataItems.Count == 0.
        // IOE means that Take() was called on a completed collection.
        // Some other thread can call CompleteAdding after we pass the
        // IsCompleted check but before we call Take.
        // In this example, we can simply catch the exception since the
        // loop will break on the next iteration.
        try
        {
            data = dataItems.Take();
        }
        catch (InvalidOperationException) { }

        if (data != null)
        {
            Process(data);
        }
    }
    Console.WriteLine("\r\nNo more items to take.");
});

// A simple blocking producer with no cancellation.
Task.Run(() =>
{
    while (moreItemsToAdd)
    {
        Data data = GetData();
        // Blocks if numbers.Count == dataItems.BoundedCapacity
        dataItems.Add(data);
    }
    // Let consumer know we are done.
    dataItems.CompleteAdding();
});

' A bounded collection. It can hold no more 
' than 100 items at once.
Dim dataItems = New BlockingCollection(Of Data)(100)

' A simple blocking consumer with no cancellation.
Task.Factory.StartNew(Sub()
                          While dataItems.IsCompleted = False
                              Dim dataItem As Data = Nothing
                              Try
                                  dataItem = dataItems.Take()
                              Catch e As InvalidOperationException
                                  ' IOE means that Take() was called on a completed collection.
                                  ' In this example, we can simply catch the exception since the 
                                  ' loop will break on the next iteration.
                              End Try
                              If (dataItem IsNot Nothing) Then
                                  Process(dataItem)
                              End If
                          End While
                          Console.WriteLine(vbCrLf & "No more items to take.")
                      End Sub)

' A simple blocking producer with no cancellation.
Task.Factory.StartNew(Sub()
                          While moreItemsToAdd = True
                              Dim item As Data = GetData()

                              ' Blocks if dataItems.Count = dataItems.BoundedCapacity.
                              dataItems.Add(item)
                          End While

                          ' Let consumer know we are done.
                          dataItems.CompleteAdding()
                      End Sub)

コード例全体については、「方法: BlockingCollection の項目を個別に追加および取得する」を参照してください。For a complete example, see How to: Add and Take Items Individually from a BlockingCollection.

時間制限付きのブロッキング操作Timed Blocking Operations

境界のあるコレクションにおける時間制限付きの TryAdd 操作および TryTake 操作では、メソッドは項目を追加または取得しようと試みます。In timed blocking TryAdd and TryTake operations on bounded collections, the method tries to add or take an item. 項目を使用できる場合、その項目は参照渡しで渡された変数に格納され、メソッドは true を返します。If an item is available it is placed into the variable that was passed in by reference, and the method returns true. 指定されたタイムアウト期間を経過しても項目が取得されない場合、メソッドは false を返します。If no item is retrieved after a specified time-out period the method returns false. その後、再びコレクションへのアクセスを試みる前に、他の必要な処理をスレッドで自由に実行できます。The thread is then free to do some other useful work before trying again to access the collection. 時間制限付きアクセス ブロックの例については、「方法: BlockingCollection の項目を個別に追加および取得する」の 2 つ目の例を参照してください。For an example of timed blocking access, see the second example in How to: Add and Take Items Individually from a BlockingCollection.

追加操作と取得操作の取り消しCancelling Add and Take Operations

追加操作と取得操作は、通常、ループ内で実行されます。Add and Take operations are typically performed in a loop. TryAdd メソッドまたは TryTake メソッドに CancellationToken を渡し、各イテレーションでトークンの IsCancellationRequested プロパティの値を確認するようにすると、ループを取り消すことができます。You can cancel a loop by passing in a CancellationToken to the TryAdd or TryTake method, and then checking the value of the token's IsCancellationRequested property on each iteration. 値が true である場合は、キャンセル要求に応答するかどうかを決定できます。応答するには、リソースをクリーンアップし、ループを終了します。If the value is true, then it is up to you to respond the cancellation request by cleaning up any resources and exiting the loop. 次の例は、キャンセル トークンを受け取る TryAdd のオーバーロードと、それを使用するコードを示しています。The following example shows an overload of TryAdd that takes a cancellation token, and the code that uses it:

do
{
    // Cancellation causes OCE. We know how to handle it.
    try
    {
        success = bc.TryAdd(itemToAdd, 2, ct);
    }
    catch (OperationCanceledException)
    {
        bc.CompleteAdding();
        break;
    }
    //...
} while (moreItems == true);
Do While moreItems = True
    ' Cancellation causes OCE. We know how to handle it.
    Try
        success = bc.TryAdd(itemToAdd, 2, ct)
    Catch ex As OperationCanceledException
        bc.CompleteAdding()
        Exit Do
    End Try
Loop

キャンセル サポートを追加する方法の例については、「方法: BlockingCollection の項目を個別に追加および取得する」の 2 つ目の例を参照してください。For an example of how to add cancellation support, see the second example in How to: Add and Take Items Individually from a BlockingCollection.

コレクションの型の指定Specifying the Collection Type

BlockingCollection<T> の作成時には、容量の上限だけでなく、使用するコレクションの型も指定できます。When you create a BlockingCollection<T>, you can specify not only the bounded capacity but also the type of collection to use. たとえば、先入れ先出し法 (FIFO) に ConcurrentQueue<T> を指定したり、後入れ先出し法 (LIFO) に ConcurrentStack<T> を指定したりできます。For example, you could specify a ConcurrentQueue<T> for first in-first out (FIFO) behavior, or a ConcurrentStack<T> for last in-first out (LIFO) behavior. IProducerConsumerCollection<T> インターフェイスを実装する任意のコレクション クラスを使用できます。You can use any collection class that implements the IProducerConsumerCollection<T> interface. BlockingCollection<T> の既定のコレクション型は ConcurrentQueue<T> です。The default collection type for BlockingCollection<T> is ConcurrentQueue<T>. 次のコード例は、1000 という容量を持ち、ConcurrentBag<T> を使用する文字列の BlockingCollection<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>:

Dim bc = New BlockingCollection(Of String)(New ConcurrentBag(Of String()), 1000)  
BlockingCollection<string> bc = new BlockingCollection<string>(new ConcurrentBag<string>(), 1000 );  

詳細については、「方法:境界ブロッキング機能をコレクションに追加する」を参照してください。For more information, see How to: Add Bounding and Blocking Functionality to a Collection.

IEnumerable のサポートIEnumerable Support

BlockingCollection<T>GetConsumingEnumerable メソッドを提供します。これにより、コンシューマーは foreach (Visual Basic では For Each) を使用し、コレクションが完了するまで (コレクションが空になり、それ以上項目が追加されなくなるまで)、項目を削除できます。BlockingCollection<T> provides a GetConsumingEnumerable method that enables consumers to use foreach (For Each in Visual Basic) to remove items until the collection is completed, which means it is empty and no more items will be added. 詳細については、「方法:ForEach を使用して BlockingCollection 内の項目を削除する」を参照してください。For more information, see How to: Use ForEach to Remove Items in a BlockingCollection.

多数の BlockingCollection を 1 つとして使用するUsing Many BlockingCollections As One

consumer が複数のコレクションから同時に項目を取得する必要のあるシナリオでは、BlockingCollection<T> の配列を作成し、TakeFromAnyAddToAny などの静的メソッドを使用できます。これらのメソッドでは、配列内の任意のコレクションを対象に追加または取得の操作を実行できます。For scenarios in which a consumer needs to take items from multiple collections simultaneously, you can create arrays of BlockingCollection<T> and use the static methods such as TakeFromAny and AddToAny that will add to or take from any of the collections in the array. いずれかのコレクションがブロックしている場合、メソッドはすぐに別のコレクションを試します。これは、操作を実行できるコレクションが見つかるまで続行されます。If one collection is blocking, the method immediately tries another until it finds one that can perform the operation. 詳細については、「方法:パイプラインでブロッキング コレクションの配列を使用する」を参照してください。For more information, see How to: Use Arrays of Blocking Collections in a Pipeline.

関連項目See also