Partager via


Vue d'ensemble de BlockingCollection

BlockingCollection<T> est une classe de collection thread-safe qui fournit les fonctionnalités suivantes :

  • Implémentation du modèle de producteur-consommateur.

  • Ajout et reprise d'éléments simultanés à partir de plusieurs threads.

  • Capacité maximale facultative.

  • Opérations d'insertion et de suppression qui se bloquent lorsque la collection est vide ou complète.

  • Opérations « d'essai » d'insertion et de suppression qui ne se bloquent pas ou qui bloquent jusqu'à une période spécifiée dans le temps.

  • Encapsule tout type de collection qui implémente IProducerConsumerCollection<T>

  • Annulation avec jetons d'annulation.

  • Deux genres d'énumérations avec foreach (For Each en Visual Basic) :

    1. Énumération en lecture seule.

    2. Énumération qui supprime des éléments à mesure qu'ils sont énumérés.

Prise en charge de la délimitation et du blocage

BlockingCollection<T> prend en charge la délimitation et le blocage. La délimitation signifie que vous pouvez définir la capacité maximale de la collection. La délimitation est importante dans certains scénarios parce qu'elle vous permet de contrôler la taille maximale de la collection en mémoire, et qu'elle empêche les threads producteurs de trop s'éloigner des threads consommateurs.

Plusieurs threads ou tâches peuvent ajouter simultanément des éléments à la collection, et si la collection atteint sa capacité maximale spécifiée, les threads producteurs se bloqueront jusqu'à ce qu'un élément soit supprimé. Plusieurs consommateurs peuvent supprimer des éléments simultanément, et si la collection devient vide, les threads consommateurs se bloqueront jusqu'à ce qu'un producteur ajoute un élément. Un thread producteur peut appeler CompleteAdding pour indiquer qu'aucun autre élément ne sera ajouté. Les consommateurs surveillent la propriété IsCompleted pour savoir quand la collection est vide et quand aucun autre élément ne sera plus ajouté. L'exemple suivant affiche un BlockingCollection simple avec une capacité limitée à 100. Une tâche de producteur ajoute des éléments à la collection tant qu'une certaine condition externe est remplie, puis appelle CompleteAdding. La tâche du consommateur prend les éléments jusqu'à ce que la propriété IsCompleted ait pour valeur True.

' 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)
            // 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.Factory.StartNew(() => 
            {
                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.Factory.StartNew(() =>
            {
                while (moreItemsToAdd)
                {
                    Data data = GetData();
                    // Blocks if numbers.Count == dataItems.BoundedCapacity
                    dataItems.Add(data);
                }
                // Let consumer know we are done.
                dataItems.CompleteAdding();
            });

Pour obtenir un exemple complet, consultez Comment : ajouter et prendre des éléments individuellement dans un BlockingCollection.

Opérations de blocage chronométré

Lors d'opérations de blocage chronométré TryAdd et TryTake sur les collections délimitées, la méthode essaie d'ajouter ou de prendre un élément. Si un élément est disponible, il est placé dans la variable passée par référence, et la méthode retourne la valeur True. Si aucun élément n'est extrait après un délai d'attente spécifié, la méthode retourne la valeur False. Le thread est ensuite libre d'accomplir un autre travail utile avant de réessayer d'accéder à la collection. Pour obtenir un exemple d'accès bloquant chronométré, consultez le deuxième exemple dans Comment : ajouter et prendre des éléments individuellement dans un BlockingCollection.

Annulation des opérations d'ajout et de reprise

Les opérations d'ajout et de reprise sont généralement exécutées dans une boucle. Vous pouvez annuler une boucle en passant un CancellationToken à la méthode TryAdd ou TryTakepuis en vérifiant la valeur de la propriété IsCancellationRequested du jeton pour chaque itération. Si la valeur est True, vous pouvez, si vous le souhaitez, répondre à la demande d'annulation en nettoyant toutes les ressources et en quittant la boucle. L'exemple suivant montre une surcharge de TryAdd qui prend un jeton d'annulation et le code qui l'utilise :

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
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);

Pour obtenir un exemple sur la façon d'ajouter la prise en charge de l''annulation, consultez le deuxième exemple dans Comment : ajouter et prendre des éléments individuellement dans un BlockingCollection.

Spécification du type de collection

Lorsque vous créez une BlockingCollection<T>, vous pouvez spécifier non seulement la capacité limitée mais aussi le type de collection à utiliser. Par exemple, vous pourriez spécifier un ConcurrentConcurrentQueue pour le comportement de premier entré premier sorti (FIFO, First-In-First-Out), ou un ConcurrentStack<T> pour le comportement de dernier entré premier sorti (LIFO, Last-In-First-Out). Vous pouvez utiliser toute classe de collection qui implémente l'interface IProducerConsumerCollection<T>. Le type de collection par défaut pour BlockingCollection<T> est ConcurrentQueue<T>. L'exemple de code suivant indique comment créer une BlockingCollection<T> de chaînes avec une capacité de 1000 et utiliser un ConcurrentBag :

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

Pour plus d'informations, consultez Comment : ajouter des fonctionnalités de liaison et de blocage à une classe de collection.

Prise en charge d'IEnumerable

BlockingCollection<T> fournit une méthode GetConsumingEnumerable qui permet aux consommateurs d'utiliser foreach (For Each dans Visual Basic) pour supprimer des éléments jusqu'à ce que la collection soit effectuée, ce qui signifie qu'il est vide et qu'aucun autre élément ne sera ajouté. Pour plus d'informations, consultez Comment : utiliser la boucle ForEach pour supprimer les éléments d'un BlockingCollection.

Utilisation de nombreux BlockingCollections en tant qu'un

Pour les scénarios dans lesquels un consommateur doit prendre simultanément des éléments à partir de plusieurs collections, vous pouvez créer des tableaux de BlockingCollection<T> et utiliser les méthodes statiques telles que TakeFromAny et AddToAny qui ajouteront ou prendront à partir de chacune des collections du tableau. Si une collection se bloque, la méthode en essaie immédiatement une autre jusqu'à ce qu'elle en trouve une qui soit susceptible d'exécuter l'opération. Pour plus d'informations, consultez Comment : utiliser des tableaux de collections de blocage dans un pipeline.

Voir aussi

Référence

System.Collections.Concurrent

Concepts

Collections thread-safe

Autres ressources

Collections et structures de données