Visão geral do BlockingCollection

BlockingCollection<T>é uma classe de coleção de thread-safe que fornece os seguintes recursos:

  • Uma implementação do padrão de produtor do consumidor.

  • Adicionando e tirar de itens de vários threads simultâneas.

  • Capacidade máxima de opcional.

  • Operações de inserção e remoção bloqueiam quando a coleção está vazia ou cheia.

  • Inserção e remoção de "tentar" operações que não bloqueie ou que bloquear até um período de tempo especificado.

  • Encapsula qualquer tipo de coleção que implementeIProducerConsumerCollection<T>

  • Cancelamento com tokens de cancelamento.

  • Dois tipos de enumeração com foreach (For Each em Visual Basic):

    1. Enumeração de somente leitura.

    2. Enumeração que remove itens conforme eles são enumerados.

Delimitadora e o bloqueio de suporte

BlockingCollection<T>suporta delimitadora e o bloqueio. Delimitadora significa que você pode definir a capacidade máxima da coleção. É importante em determinados cenários delimitadora porque ele lhe permite controlar o tamanho máximo da coleção na memória e impede que os threads produzindo movendo muito à frente dos threads consumindo.

Vários threads ou tarefas podem adicionar itens à coleção simultaneamente, e se a coleção atingir sua capacidade máxima especificada, produzindo threads irá bloquear até que um item é removido. Vários consumidores podem remover itens simultaneamente e, se a coleção ficar vazia, consumindo threads irá bloquear até que um produtor adiciona um item. Um thread produtora pode chamar CompleteAdding para indicar que não há mais itens serão adicionados. Monitor de consumidores do IsCompleted propriedade saber quando a coleção está vazia e não há mais itens serão adicionados. O exemplo a seguir mostra um simple BlockingCollection com uma capacidade limitada de 100. Uma tarefa de produtor adiciona itens à coleção, desde que alguma condição externa é verdadeira e se chama CompleteAdding. A tarefa de consumidor usa itens até o IsCompleted propriedade é 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();
            });

Para obter um exemplo completo, consulte Como: Adicionar e levar os itens individualmente a partir de um BlockingCollection.

Tempo limite de bloqueio de operações

No tempo limite de bloqueio de TryAdd e TryTake operações em coleções limitadas, o método tenta adicionar ou tirar um item. Se houver um item é colocado na variável que foi passada por referência e o método retorna true. Se nenhum item for recuperado após um período de tempo limite especificado, o método retorna false. O thread fica livre para fazer outro trabalho útil antes de tentar novamente acessar a coleção. Para obter um exemplo de acesso com tempo de bloqueio, consulte o segundo exemplo no Como: Adicionar e levar os itens individualmente a partir de um BlockingCollection.

Cancelando a adicionar e levar a operações

Adicione e operações de Take geralmente são realizadas em um loop. Você pode cancelar um loop, passando um CancellationToken para o TryAdd ou TryTake método e verificando o valor do token de IsCancellationRequested propriedade em cada iteração. Se o valor for true, em seguida, é você responder a solicitação de cancelamento, quaisquer recursos de limpeza e sair do loop. O exemplo a seguir mostra uma sobrecarga de TryAdd que leva um cancelamento token e o código que usa:

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

Para obter um exemplo de como adicionar suporte ao cancelamento, consulte o segundo exemplo no Como: Adicionar e levar os itens individualmente a partir de um BlockingCollection.

Especificando o tipo de coleção

Quando você cria um BlockingCollection<T>, você pode especificar não apenas a capacidade limitada, mas também o tipo de coleção para uso. Por exemplo, você poderia especificar um ConcurrentConcurrentQueue para a primeiro no primeiro out comportamento (FIFO), ou um ConcurrentStack<T> para o último em-first-out (LIFO) de comportamento. Você pode usar qualquer classe de coleção que implementa o IProducerConsumerCollection<T> interface. O tipo de coleção padrão de BlockingCollection<T> é ConcurrentQueue<T>. O exemplo de código a seguir mostra como criar um BlockingCollection<T> de seqüências de caracteres que tem uma capacidade de 1000 e usa um ConcurrentBag:

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

Para obter mais informações, consulte Como: Adicionar delimitadora e a funcionalidade de uma classe de coleção de bloqueio.

Suporte de IEnumerable

BlockingCollection<T>Fornece um GetConsumingEnumerable método que permite que os consumidores usar foreach (For Each na Visual Basic) para remover itens até que a coleção seja concluída, que significa que ele está vazio e não há mais itens serão adicionados. Para obter mais informações, consulte Como: Usar o ForEach para remover os itens em um BlockingCollection.

Usando o BlockingCollections muitos como um

Para cenários em que um consumidor precisa levar os itens de várias coleções simultaneamente, você pode criar matrizes de BlockingCollection<T> e usar os métodos estáticos, como TakeFromAny e AddToAny que irá adicionar ou levar a partir de qualquer uma das coleções da matriz. Se o bloqueio de uma coleção, o método imediatamente tenta outra até encontrar uma que pode executar a operação. Para obter mais informações, consulte Como: Usar matrizes de bloqueio de coleções em um Pipeline.

Consulte também

Referência

System.Collections.Concurrent

Conceitos

Coleções e Estruturas de Dados

Coleções de thread-Safe.