Sincronização de dados para multithreading

Quando múltiplos threads podem fazer chamadas às propriedades e métodos de um único objeto, é fundamental que essas chamadas estejam sincronizadas. Caso contrário, um thread pode interromper o que outro thread está a fazer, e o objeto pode ficar num estado inválido. Uma classe cujos membros estão protegidos contra tais interrupções é chamada thread-safe.

O .NET fornece várias estratégias para sincronizar o acesso a membros de instância e estáticos:

  • Regiões de código sincronizadas. Pode usar a classe Monitor ou o suporte do compilador para esta classe para sincronizar apenas o bloco de código que dela necessita, melhorando o desempenho.

  • Sincronização manual. Pode usar os objetos de sincronização fornecidos pela biblioteca de classes .NET. Consulte a Visão Geral das Primitivas de Sincronização, que inclui uma discussão sobre a Monitor classe.

  • Contextos sincronizados. Para aplicações .NET Framework apenas, pode usar o SynchronizationAttribute para ativar a sincronização simples e automática dos ContextBoundObject objetos.

  • Classes de coleção no System.Collections.Concurrent namespace. Estas classes fornecem operações sincronizadas de adição e remoção integradas. Para obter mais informações, consulte Thread-Safe Coleções.

O runtime da common language fornece um modelo de thread no qual as classes se enquadram em várias categorias que podem ser sincronizadas de várias formas, dependendo dos requisitos. A tabela seguinte mostra que suporte de sincronização é fornecido para campos e métodos com uma dada categoria de sincronização.

Category Campos globais Campos estáticos Métodos estáticos Campos de instância Métodos de instância Blocos de código específicos
Sem sincronização Não Não Não Não Não Não
Contexto Sincronizado Não Não Não Yes Yes Não
Regiões de Código Sincronizadas Não Não Apenas se estiver assinalado Não Apenas se estiver assinalado Apenas se estiver assinalado
Sincronização Manual Manual Manual Manual Manual Manual Manual

Sem sincronização

Este é o padrão para objetos. Qualquer thread pode aceder a qualquer método ou campo a qualquer momento. Apenas um thread de cada vez deve aceder a estes objetos.

Sincronização manual

A biblioteca de classes .NET fornece várias classes para sincronizar threads. Ver Visão Geral das Primitivas de Sincronização.

Regiões de código sincronizadas

Pode usar a Monitor classe ou uma palavra-chave do compilador para sincronizar blocos de código, métodos de instância e métodos estáticos. Não há suporte para campos estáticos sincronizados.

Tanto o Visual Basic como o C# suportam a marcação de blocos de código com uma palavra-chave específica da linguagem, a lock instrução em C# ou a SyncLock instrução no Visual Basic. Quando o código é executado por um thread, é feita uma tentativa de adquirir o bloqueio. Se o bloqueio já tiver sido adquirido por outra thread, a thread fica bloqueada até que o bloqueio esteja disponível. Quando o thread sai do bloco sincronizado de código, o bloqueio é libertado, independentemente de como o thread sai do bloco.

Note

No .NET 9 e no C# 13 ou posterior, se o destino lock for uma instância dedicada System.Threading.Lock, a instrução lock utiliza Lock.EnterScope() para criar uma região sincronizada.

O Visual Basic não suporta System.Threading.Lock em SyncLock, por isso continue a usar SyncLock com um tipo de referência privado dedicado.

Se o destino lock não for uma instância de Lock, e em toda a utilização de SyncLock, o compilador utiliza Monitor.Enter e Monitor.Exit. Por causa dessa implementação, ainda podes usar outros Monitor métodos na região sincronizada.

Também pode decorar um método com um MethodImplAttribute valor de MethodImplOptions.Synchronized, o que tem o mesmo efeito que usar Monitor ou uma das palavras-chave do compilador para bloquear todo o corpo do método.

Thread.Interrupt pode ser usado para interromper um thread de execução que esteja em operações de bloqueio, como a espera de acesso a uma secção sincronizada de código. Thread.Interrupt também é utilizado para interromper threads durante operações como Thread.Sleep.

Important

Não bloqueie o tipo — ou seja, typeof(MyType) em C#, GetType(MyType) no Visual Basic ou MyType::typeid em C++ — para proteger static métodos (Sharedmétodos no Visual Basic). Use antes um objeto estático privado. Da mesma forma, não uses this em C# (Meno Visual Basic) para bloquear métodos de instância. Usa antes um objeto privado. Uma classe ou instância pode ser bloqueada por código diferente do seu, podendo causar bloqueios ou problemas de desempenho.

Suporte a compiladores

Tanto o Visual Basic como o C# suportam uma palavra-chave de linguagem que utiliza Monitor.Enter e Monitor.Exit bloqueia o objeto. O Visual Basic suporta a instrução SyncLock; C# suporta a instrução lock.

Em ambos os casos, se for gerada uma exceção no bloco de código, o bloqueio adquirido com lock ou SyncLock é libertado automaticamente. Os compiladores C# e Visual Basic emitem um bloco try/finally com Monitor.Enter no início do bloco try e Monitor.Exit no bloco finally. Se for lançada uma exceção dentro do bloco lock ou SyncLock, o processador finally é executado para lhe permitir efetuar quaisquer operações de limpeza.

Contexto Sincronizado

Apenas em aplicações do .NET Framework, pode utilizar o SynchronizationAttribute em qualquer ContextBoundObject para sincronizar todos os métodos e campos de instância. Todos os objetos no mesmo domínio de contexto partilham o mesmo bloqueio. Múltiplos threads são permitidos para aceder aos métodos e campos, mas apenas um único thread é permitido de cada vez.

Consulte também