Übersicht über BlockingCollection

<xref:System.Collections.Concurrent.BlockingCollection%601> ist eine threadsichere Auflistungsklasse, die die folgenden Features bietet:

  • Eine Implementierung des Producer-Consumer-Musters.

  • Gleichzeitiges Hinzufügen und Entfernen von Elementen aus mehreren Threads.

  • Optionale maximale Kapazität.

  • Einfüge- und Löschvorgänge, die blockiert werden, wenn die Auflistung leer oder voll ist.

  • „Versuchs“-Einfüge- und Löschvorgänge, die nicht oder nur für eine angegebene Zeitspanne blockiert werden.

  • Kapselt jeden Auflistungstyp, der <xref:System.Collections.Concurrent.IProducerConsumerCollection%601> implementiert.

  • Abbruch mit Abbruchtoken.

  • Zwei Arten von Enumeration mit foreach (For Each in Visual Basic):

    1. Schreibgeschützte Enumeration.

    2. Enumeration, in deren Verlauf Elemente entfernt werden.

Unterstützung von Begrenzen und Blockieren

<xref:System.Collections.Concurrent.BlockingCollection%601> unterstützt das Begrenzen und Blockieren Begrenzen bedeutet, dass Sie die maximale Kapazität der Auflistung festlegen können. Begrenzen ist in bestimmten Szenarien wichtig, da es Ihnen ermöglicht, die maximale Größe der Auflistung im Arbeitsspeicher zu steuern, und es verhindert, dass die erzeugenden Threads sich zu weit vor die verbrauchenden Threads bewegen.

Mehrere Threads oder Tasks können gleichzeitig der Auflistung Elemente hinzufügen, und wenn die Auflistung die angegebene maximale Kapazität erreicht, werden die erzeugenden Threads blockiert, bis ein Element entfernt wird. Mehrere Consumer können gleichzeitig Elemente entfernen, und wenn die Auflistung leer ist, werden die verbrauchenden Threads blockiert, bis ein Producer ein Element hinzufügt. Ein Producer-Thread kann <xref:System.Collections.Concurrent.BlockingCollection%601.CompleteAdding%2A> aufrufen, um anzuzeigen, dass keine Elemente mehr hinzugefügt werden. Consumer überwachen die <xref:System.Collections.Concurrent.BlockingCollection%601.IsCompleted%2A>-Eigenschaft, um zu wissen, wann die Auflistung leer ist, und keine Elemente mehr hinzugefügt werden. Das folgende Beispiel zeigt eine einfache BlockingCollection mit einer begrenzten Kapazität von 100. Ein Producer-Task fügt der Auflistung Elemente hinzu, solange eine externe Bedingung wahr ist, und ruft dann <xref:System.Collections.Concurrent.BlockingCollection%601.CompleteAdding%2A> auf. Der Consumer-Task entnimmt Elemente, bis die <xref:System.Collections.Concurrent.BlockingCollection%601.IsCompleted%2A>-Eigenschaft wahr ist.

// 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 number.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 numbers.Count = dataItems.BoundedCapacity
                              dataItems.Add(item)
                          End While

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

Ein vollständiges Beispiel finden Sie unter Gewusst wie: Hinzufügen und Entfernen von einzelnen Elementen zu bzw. aus einer BlockingCollection.

Zeitgesteuerte Blockierungsvorgänge

In zeitgesteuerten <xref:System.Collections.Concurrent.BlockingCollection%601.TryAdd%2A>- und <xref:System.Collections.Concurrent.BlockingCollection%601.TryTake%2A>-Blockierungsvorgängen, die für begrenzte Sammlungen durchgeführt werden, versucht die Methode, ein Element hinzuzufügen oder zu entnehmen. Wenn ein Element verfügbar ist, wird es in die Variable eingesetzt, die durch Verweis übergeben wurde, und die Methode gibt WAHR zurück. Wenn nach einer angegebenen Timeoutfrist kein Element abgerufen wird, gibt die Methode FALSCH zurück. Der Thread ist dann vor dem erneuten Versuch, auf die Auflistung zuzugreifen, frei für andere nützliche Aufgaben. Ein Beispiel für das zeitgesteuerte Blockieren des Zugriffs ist das zweite Beispiel unter Gewusst wie: Hinzufügen und Entfernen von einzelnen Elementen zu bzw. aus einer BlockingCollection.

Abbrechen von Hinzufüge- und Nehmevorgängen

Hinzufüge- und Nehmevorgänge werden in der Regel in einer Schleife ausgeführt. Sie können eine Schleife durch die Übergabe eines <xref:System.Threading.CancellationToken> an die <xref:System.Collections.Concurrent.BlockingCollection%601.TryAdd%2A>- oder <xref:System.Collections.Concurrent.BlockingCollection%601.TryTake%2A>-Methode abbrechen und dann den Wert der <xref:System.Threading.CancellationToken.IsCancellationRequested%2A>-Eigenschaft des Tokens in jeder Iteration überprüfen. Wenn der Wert WAHR ist, liegt es an Ihnen, auf die Abbruchanforderung durch Bereinigen aller Ressourcen und Beenden der Schleife zu reagieren. Das folgende Beispiel zeigt eine Überladung von <xref:System.Collections.Concurrent.BlockingCollection%601.TryAdd%2A>, die ein Abbruchtoken entgegennimmt, und den Code, der es verwendet:

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

Ein Beispiel für das Hinzufügen von Abbruchunterstützung ist das zweite Beispiel unter Gewusst wie: Hinzufügen und Entfernen von einzelnen Elementen zu bzw. aus einer BlockingCollection.

Angeben des Auflistungstyps

Beim Erstellen einer <xref:System.Collections.Concurrent.BlockingCollection%601> können Sie nicht nur die begrenzte Kapazität angeben, sondern auch den Typ der zu verwendenden Auflistung. Sie könnten z.B. einen <xref:System.Collections.Concurrent.ConcurrentQueue%601> für das First-in-First-out-Verhalten (FIFO) oder einen <xref:System.Collections.Concurrent.ConcurrentStack%601> für das Last-in-First-out-Verhalten (LIFO) angeben. Sie können jede Auflistungsklasse verwenden, die die <xref:System.Collections.Concurrent.IProducerConsumerCollection%601>-Schnittstelle implementiert. Der standardmäßige Auflistungstyp für <xref:System.Collections.Concurrent.BlockingCollection%601> ist <xref:System.Collections.Concurrent.ConcurrentQueue%601>. Das folgende Codebeispiel veranschaulicht das Erstellen einer <xref:System.Collections.Concurrent.BlockingCollection%601> von Zeichenfolgen, die eine Kapazität von 1000 hat und einen <xref:System.Collections.Concurrent.ConcurrentBag%601> verwendet:

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

Weitere Informationen finden Sie unter Gewusst wie: Hinzufügen von Begrenzungs- und Blockadefunktionen zu einer Auflistung.

IEnumerable-Unterstützung

<xref:System.Collections.Concurrent.BlockingCollection%601> bietet eine <xref:System.Collections.Concurrent.BlockingCollection%601.GetConsumingEnumerable%2A>-Methode, mit der Consumer eine foreach (For Each in Visual Basic) verwenden können, um Elemente zu entfernen, bis die Sammlung abgeschlossen ist, d.h. bis sie leer ist und keine Elemente mehr hinzugefügt werden. Weitere Informationen finden Sie unter Gewusst wie: Entfernen von Elementen in einer BlockingCollection mit ForEach.

Verwenden vieler BlockingCollections als eine einzige

Für Szenarien, in denen ein Consumer Elemente aus mehreren Auflistungen gleichzeitig nehmen muss, können Sie Arrays von <xref:System.Collections.Concurrent.BlockingCollection%601> erstellen und statische Methoden wie <xref:System.Collections.Concurrent.BlockingCollection%601.TakeFromAny%2A> und <xref:System.Collections.Concurrent.BlockingCollection%601.AddToAny%2A> verwenden, die Elemente beliebigen Auflistungen im Array hinzufügen bzw. daraus entnehmen. Wenn eine Auflistung blockiert wird, versucht die Methode sofort, eine andere zu verwenden, bis sie eine findet, die den Vorgang ausführen kann. Weitere Informationen finden Sie unter Gewusst wie: Verwenden von Arrays mit blockierenden Auflistungen in einer Pipeline.

Siehe auch

<xref:System.Collections.Concurrent?displayProperty=fullName>
Auflistungen und Datenstrukturen
threadsichere Auflistungen