ConcurrentBag: Coleção Concorrente Não Ordenada para Processamento Paralelo | CSharp
- ConcurrentBag: Coleção Concorrente Não Ordenada para Processamento Paralelo | CSharp
ConcurrentBag in C# - Code Maze: 🔗 Clique aqui
Em aplicações multithreaded, a escolha correta das estruturas de dados pode ser determinante para a escalabilidade e robustez do sistema. O ConcurrentBag<T> é uma das coleções disponíveis na biblioteca base do .NET que oferece acesso concorrente seguro com um custo relativamente baixo de sincronização. Neste artigo, vamos entender quando e por que usar o ConcurrentBag, suas principais características, vantagens, limitações e cenários adequados de uso.
O que é o ConcurrentBag?
O ConcurrentBag<T> é uma coleção genérica que pertence ao namespace System.Collections.Concurrent. Seu principal propósito é armazenar itens de forma não ordenada, permitindo que múltiplas threads adicionem e removam elementos simultaneamente, sem a necessidade de implementar mecanismos manuais de sincronização.
Essa estrutura implementa a interface IProducerConsumerCollection<T>, o que significa que ela é adequada para padrões de produtor/consumidor, embora não garanta nenhuma ordenação na inserção ou retirada dos elementos.
Características principais
- Thread-safe: todas as operações são seguras em ambientes concorrentes.
- Sem ordenação: a ordem dos elementos não é garantida, nem na adição, nem na remoção.
- Acesso indireto: não há indexação, ou seja, não é possível acessar elementos por posição.
- Desempenho otimizado: utiliza mecanismos lock-free internos, o que pode levar a uma performance superior em determinados cenários.
Como usar o ConcurrentBag
1. Criação
ConcurrentBag<int> bag = new();
Ou com valores iniciais:
var bag = new ConcurrentBag<int> { 1, 2, 3, 4 };
Ou ainda, a partir de outra coleção:
var lista = new List<int> { 1, 2, 3 };
var bag = new ConcurrentBag<int>(lista);
2. Adição de elementos (Add)
bag.Add(42);
3. Remoção de elementos (TryTake)
if (bag.TryTake(out int item))
{
// item removido com sucesso
}
4. Visualização sem remoção (TryPeek)
if (bag.TryPeek(out int item))
{
// item lido, mas não removido
}
Concorrência na prática
A principal vantagem do ConcurrentBag se manifesta em processamentos paralelos, onde vários produtores e consumidores trabalham simultaneamente.
Exemplo: Adição concorrente
ConcurrentBag<int> bag = new();
Parallel.For(0, 1000, i => bag.Add(i));
Exemplo: Remoção concorrente
Parallel.For(0, 100, i =>
{
if (bag.TryTake(out int numero))
{
Console.WriteLine($"Thread {Thread.CurrentThread.ManagedThreadId} removeu {numero}");
}
});
Esses exemplos mostram como podemos manipular a coleção de forma paralela sem nos preocupar com locks explícitos ou condições de corrida.
Métodos úteis
ToArray()– retorna os itens como um array.CopyTo(array, index)– copia os itens para um array existente.Clear()– remove todos os elementos da coleção.IsEmpty– verifica se a coleção está vazia.Count– retorna o número de elementos (pode ser impreciso em concorrência extrema).
Quando usar ConcurrentBag
✅ Cenários apropriados:
- Quando múltiplas threads precisam acessar (ler/gravar) uma coleção simultaneamente.
- Quando a ordem dos elementos não importa.
- Quando se busca um bom compromisso entre performance e simplicidade.
❌ Evite usar se:
- A ordem dos elementos for relevante (use
ConcurrentQueueouConcurrentStack). - For necessário acesso direto por índice.
- Precisar de controle mais fino sobre iteração ou ordenação.
Vantagens
- Implementação pronta e otimizada da .NET.
- Permite processamento paralelo eficiente sem complexidade adicional.
- Desempenho superior em cenários com alta taxa de inserção/remoção.
Desvantagens
- Não garante ordem dos dados.
- Não suporta indexadores.
- Pouco adequada para casos onde os itens precisam ser processados em sequência.
Conclusão
O ConcurrentBag<T> é uma estrutura poderosa para aplicações que envolvem múltiplas threads interagindo com uma coleção de dados de maneira dinâmica e sem a necessidade de preservação de ordem. Seu uso pode simplificar a implementação e melhorar o desempenho de sistemas multithreaded.
Entretanto, como toda abstração, deve ser utilizada com entendimento claro de suas limitações. A escolha da estrutura de dados correta é fundamental para manter a coerência, a performance e a previsibilidade do comportamento da aplicação.
Se o seu cenário se encaixa nesse perfil – múltiplos acessos concorrentes e desprezo pela ordem – o ConcurrentBag é uma escolha certeira.