Coleções thread-safe

O .NET Framework 4 introduz o namespace System.Collections.Concurrent, que inclui várias classes de coleção que são tanto thread-safe quanto escalonáveis. Vários threads podem adicionar ou remover itens dessas coleções de modo seguro e eficiente sem a necessidade de sincronização adicional no código do usuário. Ao escrever um novo código, use as classes de coleção simultâneas sempre que vários threads forem ser gravados simultaneamente nas coleções. Se estiver lendo apenas de uma coleção compartilhada, você poderá usar as classes no namespace System.Collections.Generic. É recomendável não usar as classes da coleção 1.0, a menos que você precise ter como destino o runtime do .NET Framework 1.1 ou anterior.

Sincronização de thread nas coleções do .NET Framework 1.0 e 2.0

As coleções introduzidas no .NET Framework 1.0 são encontradas no namespace System.Collections. Essas coleções, que incluem os comumente usados ArrayList e Hashtable, fornecem algum acesso thread-safe por meio da propriedade Synchronized, que retorna um wrapper thread-safe em torno da coleção. O wrapper funciona bloqueando toda a coleção a cada operação de adição ou remoção. Portanto, cada thread que está tentando acessar a coleção deve aguardar por sua vez para receber esse bloqueio. Isso não é escalonável e pode causar degradação significativa no desempenho para coleções grandes. Além disso, o design não é totalmente protegido contra condições de corrida. Para saber mais, confira Synchronization in Generic Collections (Sincronização em coleções genéricas).

As classes de coleções introduzidas no .NET Framework 2.0 são encontradas no namespace System.Collections.Generic. Entre elas List<T>, Dictionary<TKey,TValue> e assim por diante. Essas classes fornecem desempenho e segurança de tipos aprimorados em comparação com as classes do .NET Framework 1.0. No entanto, as classes de coleção do .NET Framework 2.0 não fornecem nenhuma sincronização de thread; o código de usuário deve fornecer sincronização quando itens são adicionados ou removidos simultaneamente em vários threads.

O recomendável são as classes de coleções simultâneas no .NET Framework 4, pois elas fornecem não apenas a segurança de tipos das classes de coleção do .NET Framework 2.0, mas também acesso thread-safe mais eficiente e completo do que o fornecido pelas coleções do .NET Framework 1.0.

Bloqueio refinado e mecanismos sem bloqueio

Alguns dos tipos de coleção simultâneos usam mecanismos de sincronização leves como SpinLock, SpinWait, SemaphoreSlim e CountdownEvent, que são novos no .NET Framework 4. Esses tipos de sincronização geralmente usam giro ocupado por breves períodos antes de colocarem o thread em um verdadeiro estado de espera. Quando espera-se que os tempos de espera sejam muito curtos, girar é muito menos dispendioso em termos de recursos computacionais do que esperar, o que envolve uma transição de kernel que utiliza muitos recursos. Para classes de coleção que usam o giro, essa eficiência significa que vários threads podem adicionar e remover itens em uma taxa muito alta. Para saber mais sobre rotação versus bloqueio, confira SpinLock e SpinWait.

As classes ConcurrentQueue<T> e ConcurrentStack<T> não usam bloqueios. Em vez disso, elas usam operações do Interlocked para obter acesso thread-safe.

Observação

Considerando que as classes de coleções simultâneas dão suporte a ICollection, elas fornecem implementações para as propriedades IsSynchronized e SyncRoot, embora essas propriedades sejam irrelevantes. IsSynchronized sempre retorna false e SyncRoot é sempre null (Nothing no Visual Basic).

A tabela a seguir lista os tipos de coleção no namespace System.Collections.Concurrent.

Tipo Descrição
BlockingCollection<T> Fornece funcionalidade de delimitação e bloqueio de qualquer tipo que implemente IProducerConsumerCollection<T>. Para obter mais informações, veja Visão geral de BlockingCollection.
ConcurrentDictionary<TKey,TValue> Implementação thread-safe de um dicionário de pares chave-valor.
ConcurrentQueue<T> Implementação thread-safe de uma fila PEPS (primeiro a entrar, primeiro a sair).
ConcurrentStack<T> Implementação thread-safe de uma fila LIFO (último a entrar, primeiro a sair).
ConcurrentBag<T> Implementação thread-safe de uma coleção não ordenada de elementos.
IProducerConsumerCollection<T> A interface que um tipo deve implementar para uso em um BlockingCollection.
Título Descrição
Visão geral de BlockingCollection Este tópico descreve a funcionalidade fornecida pelo tipo BlockingCollection<T>.
Como: Adicionar e remover itens de um ConcurrentDictionary Descreve como adicionar e remover elementos de um ConcurrentDictionary<TKey,TValue>
Como: Adicionar e remover itens individualmente de uma BlockingCollection Descreve como adicionar e recuperar itens de uma coleta de bloqueio sem usar o enumerador de somente leitura.
Como: Adicionar a funcionalidade de delimitação e bloqueio a uma coleção Descreve como usar qualquer classe de coleção como o mecanismo de armazenamento subjacente para uma coleção IProducerConsumerCollection<T>.
Como: Usar ForEach para remover itens de uma BlockingCollection Descreve como usar foreach (For Each no Visual Basic) para remover todos os itens em uma coleção de bloqueios.
Como: Usar matrizes de coleções de bloqueio em um pipeline Descreve como usar várias coleções de bloqueio ao mesmo tempo para implementar um pipeline.
Como: Criar um pool de objetos, usando um ConcurrentBag Mostra como usar um recipiente simultâneo para melhorar o desempenho em cenários nos quais, em vez de criar novos objetos continuamente, você pode reutilizá-los.

Referência

System.Collections.Concurrent