Skip to main content

Block Collection

  • Block Collection

O BlockingCollection<T> em C# é uma classe do namespace System.Collections.Concurrent, usada para gerenciar uma coleção de itens que oferece suporte à concorrência (acesso de múltiplas threads) e ao bloqueio. Ele é frequentemente utilizado em cenários de produção e consumo de dados (produtor-consumidor), onde uma ou mais threads produzem dados e uma ou mais threads consomem esses dados.

Principais Características do BlockingCollection<T>

  • Bloqueio: Ele bloqueia automaticamente a thread que tenta adicionar ou remover itens quando a coleção atinge uma capacidade específica (opcional) ou quando está vazia, respectivamente.
  • Controle de Capacidade: Você pode definir um limite de capacidade para a coleção, útil para evitar o uso excessivo de memória quando há muitos itens na coleção.
  • Concorrência: Como ele é seguro para acesso concorrente, várias threads podem adicionar e remover itens simultaneamente, sem a necessidade de gerenciar locks explicitamente.

Cenário Típico de Uso: Padrão Produtor-Consumidor

No padrão produtor-consumidor, uma thread (ou várias) insere itens na coleção enquanto outra thread (ou várias) retira itens para processamento. O BlockingCollection permite gerenciar isso com bloqueios automáticos, simplificando a coordenação entre threads.

Criando e Usando o BlockingCollection

Aqui está um exemplo básico para ilustrar o uso do BlockingCollection em um cenário produtor-consumidor:

using System;
using System.Collections.Concurrent;
using System.Threading;
using System.Threading.Tasks;

class Program
{
static void Main()
{
// Cria uma BlockingCollection com uma capacidade máxima de 5 itens
using (var collection = new BlockingCollection<int>(5))
{
// Tarefa Produtora: Adiciona itens na coleção
Task producer = Task.Run(() =>
{
for (int i = 0; i < 10; i++)
{
Console.WriteLine($"Produzindo {i}");
collection.Add(i); // Adiciona o item na coleção (pode bloquear se a capacidade for atingida)
Thread.Sleep(500); // Simula o tempo de produção
}
collection.CompleteAdding(); // Sinaliza que não haverá mais itens
});

// Tarefa Consumidora: Consome itens da coleção
Task consumer = Task.Run(() =>
{
foreach (var item in collection.GetConsumingEnumerable())
{
Console.WriteLine($"Consumindo {item}");
Thread.Sleep(1000); // Simula o tempo de processamento
}
});

// Aguarda as tarefas completarem
Task.WaitAll(producer, consumer);
}
}
}

Explicação do Código

  1. Criação da BlockingCollection:

    • Definimos a coleção com uma capacidade de 5 itens. Isso significa que, se houver mais de 5 itens, o produtor será bloqueado até que o consumidor retire algum item.
  2. Tarefa Produtora:

    • A tarefa produtora adiciona itens à coleção com o método Add. Se a coleção atingir sua capacidade (5 itens neste caso), a thread produtora será bloqueada até que haja espaço disponível.
    • Após adicionar todos os itens (de 0 a 9), chamamos CompleteAdding(), que indica que não haverá mais itens a serem adicionados, facilitando a sinalização para o consumidor de que não precisa mais esperar.
  3. Tarefa Consumidora:

    • A tarefa consumidora consome itens da coleção usando um foreach no método GetConsumingEnumerable(). Esse método bloqueia a iteração até que um novo item esteja disponível ou até que a coleção seja sinalizada como "completa" (sem novos itens para adicionar).
    • Cada item é processado com um Thread.Sleep(1000), que simula um tempo de processamento mais demorado.

Métodos Principais do BlockingCollection

  • Add(item): Adiciona um item à coleção. Se a capacidade for atingida, a chamada será bloqueada até que haja espaço.
  • Take(): Remove e retorna um item da coleção. Se a coleção estiver vazia, a chamada será bloqueada até que haja algum item.
  • CompleteAdding(): Indica que não serão adicionados mais itens. Os consumidores serão notificados ao processarem todos os itens restantes.
  • GetConsumingEnumerable(): Retorna um IEnumerable<T> para iterar e consumir os itens. Esse método é útil para consumidores que querem processar os itens até que a coleção seja esvaziada e sinalizada como completa.

Quando Usar o BlockingCollection

Use BlockingCollection em cenários onde você precisa:

  • Implementar o padrão produtor-consumidor de forma eficiente e segura para threads.
  • Gerenciar a troca de dados entre threads, permitindo que uma thread produza dados em uma taxa diferente daquela em que outras threads os consomem.
  • Aplicar controle de capacidade a uma coleção compartilhada para limitar o número de itens (útil para evitar o consumo excessivo de recursos).

O BlockingCollection simplifica bastante a sincronização entre threads, especialmente em comparação com a necessidade de gerenciar locks, monitores ou outros mecanismos de sincronização manual.