Visão geral das primitivas de sincronização
O .NET fornece uma variedade de tipos que você pode usar para sincronizar o acesso a um recurso compartilhado ou coordenar a interação de thread.
Importante
Use a mesma instância primitiva de sincronização para proteger o acesso de um recurso compartilhado. Se você usar instâncias primitivas de sincronização diferentes para proteger o mesmo recurso, contornará a proteção fornecida por uma primitiva de sincronização.
Classe WaitHandle e tipos de sincronização leves
Várias primitivas de sincronização do .NET derivam da System.Threading.WaitHandle classe, que encapsula um identificador de sincronização nativo do sistema operacional e usa um mecanismo de sinalização para interação de thread. Essas classes incluem:
- System.Threading.Mutex, que concede acesso exclusivo a um recurso partilhado. O estado de um mutex é sinalizado se nenhum thread o possuir.
- System.Threading.Semaphore, que limita o número de threads que podem acessar um recurso compartilhado ou um pool de recursos simultaneamente. O estado de um semáforo é definido como sinalizado quando sua contagem é maior que zero e não sinalizado quando sua contagem é zero.
- System.Threading.EventWaitHandle, que representa um evento de sincronização de thread e pode estar em um estado sinalizado ou não sinalizado.
- System.Threading.AutoResetEvent, que deriva de e, quando sinalizado, redefine automaticamente para um estado não sinalizado depois de liberar um único thread de EventWaitHandle espera.
- System.Threading.ManualResetEvent, que deriva de e, quando sinalizado, permanece em um estado sinalizado EventWaitHandle até que o Reset método seja chamado.
No .NET Framework, como WaitHandle deriva de System.MarshalByRefObject, esses tipos podem ser usados para sincronizar as atividades de threads entre os limites do domínio do aplicativo.
No .NET Framework, .NET Core e .NET 5+, alguns desses tipos podem representar identificadores de sincronização do sistema nomeados, que são visíveis em todo o sistema operacional e podem ser usados para a sincronização entre processos:
- Mutex
- Semaphore (no Windows)
- EventWaitHandle (no Windows)
Para obter mais informações, consulte a referência da WaitHandle API.
Tipos de sincronização leves não dependem de identificadores subjacentes do sistema operacional e normalmente fornecem melhor desempenho. No entanto, eles não podem ser usados para a sincronização entre processos. Use esses tipos para sincronização de threads em um aplicativo.
Alguns desses tipos são alternativas aos tipos derivados de WaitHandle. Por exemplo, SemaphoreSlim é uma alternativa leve ao Semaphore.
Sincronização de acesso a um recurso compartilhado
O .NET fornece uma variedade de primitivas de sincronização para controlar o acesso a um recurso compartilhado por vários threads.
Classe Monitor
A System.Threading.Monitor classe concede acesso mutuamente exclusivo a um recurso compartilhado adquirindo ou liberando um bloqueio no objeto que identifica o recurso. Enquanto uma fechadura é segurada, a rosca que segura a fechadura pode novamente adquirir e liberar a fechadura. Qualquer outro thread é impedido de adquirir o bloqueio e o Monitor.Enter método aguarda até que o bloqueio seja liberado. O Enter método adquire um bloqueio liberado. Você também pode usar o Monitor.TryEnter método para especificar a quantidade de tempo durante a qual um thread tenta adquirir um bloqueio. Como a Monitor classe tem afinidade de thread, o thread que adquiriu um bloqueio deve liberar o bloqueio chamando o Monitor.Exit método.
Você pode coordenar a interação de threads que adquirem um bloqueio no mesmo objeto usando os Monitor.Waitmétodos , Monitor.Pulsee .Monitor.PulseAll
Para obter mais informações, consulte a referência da Monitor API.
Nota
Use a instrução lock em C# e a instrução SyncLock no Visual Basic para sincronizar o acesso a um recurso compartilhado em vez de usar a Monitor classe diretamente. Essas instruções são implementadas usando os Enter métodos e Exit e um try…finally
bloco para garantir que o bloqueio adquirido seja sempre liberado.
Classe Mutex
A System.Threading.Mutex classe, como Monitor, concede acesso exclusivo a um recurso compartilhado. Use uma das sobrecargas do método Mutex.WaitOne para solicitar a propriedade de um mutex. Como Monitor, Mutex tem afinidade de thread e o thread que adquiriu um mutex deve liberá-lo chamando o Mutex.ReleaseMutex método.
Ao contrário Monitordo , a Mutex classe pode ser usada para sincronização entre processos. Para fazer isso, use um mutex nomeado, que é visível em todo o sistema operacional. Para criar uma instância mutex nomeada, use um construtor Mutex que especifica um nome. Você também pode chamar o Mutex.OpenExisting método para abrir um mutex de sistema nomeado existente.
Para obter mais informações, consulte o artigo Mutexes e a referência da Mutex API.
Estrutura SpinLock
A System.Threading.SpinLock estrutura, como Monitor, concede acesso exclusivo a um recurso compartilhado com base na disponibilidade de um bloqueio. Quando SpinLock tenta adquirir um bloqueio que não está disponível, ele aguarda em um loop, verificando repetidamente até que o bloqueio fique disponível.
Para obter mais informações sobre os benefícios e desvantagens do uso do bloqueio de rotação, consulte o artigo SpinLock e a referência da SpinLock API.
Classe ReaderWriterLockSlim
A System.Threading.ReaderWriterLockSlim classe concede acesso exclusivo a um recurso compartilhado para escrita e permite que vários threads acessem o recurso simultaneamente para leitura. Talvez você queira usar ReaderWriterLockSlim para sincronizar o acesso a uma estrutura de dados compartilhada que ofereça suporte a operações de leitura seguras para threads, mas exija acesso exclusivo para executar a operação de gravação. Quando um thread solicita acesso exclusivo (por exemplo, chamando o método), as solicitações subsequentes de ReaderWriterLockSlim.EnterWriteLock leitor e gravador bloqueiam até que todos os leitores existentes tenham saído do bloqueio e o gravador tenha entrado e saído do bloqueio.
Para obter mais informações, consulte a referência da ReaderWriterLockSlim API.
Classes Semaphore e SemaphoreSlim
As System.Threading.Semaphore classes e System.Threading.SemaphoreSlim limitam o número de threads que podem acessar um recurso compartilhado ou um pool de recursos simultaneamente. Threads adicionais que solicitam o recurso aguardam até que qualquer thread libere o semáforo. Como o semáforo não tem afinidade com fios, um fio pode adquirir o semáforo e outro pode liberá-lo.
SemaphoreSlim é uma alternativa leve e Semaphore pode ser usada apenas para sincronização dentro de um único limite de processo.
No Windows, você pode usar Semaphore para a sincronização entre processos. Para fazer isso, crie uma Semaphore instância que representa um semáforo de sistema nomeado usando um dos construtores Semaphore que especifica um nome ou o Semaphore.OpenExisting método. SemaphoreSlim não suporta semáforos de sistema nomeados.
Para obter mais informações, consulte o artigo Semaphore e SemaphoreSlim e a referência da SemaphoreSlimSemaphore API.
Interação de thread ou sinalização
Interação de thread (ou sinalização de thread) significa que um thread deve aguardar a notificação, ou um sinal, de um ou mais threads para prosseguir. Por exemplo, se o thread A chamar o método do thread B, o thread A será bloqueado até que o Thread.Join thread B seja concluído. As primitivas de sincronização descritas na seção anterior fornecem um mecanismo diferente para sinalização: ao liberar um bloqueio, um thread notifica outro thread de que ele pode prosseguir adquirindo o bloqueio.
Esta seção descreve construções de sinalização adicionais fornecidas pelo .NET.
Classes EventWaitHandle, AutoResetEvent, ManualResetEvent e ManualResetEventSlim
A System.Threading.EventWaitHandle classe representa um evento de sincronização de thread.
Um evento de sincronização pode estar em um estado não sinalizado ou sinalizado. Quando o estado de um evento não é sinalizado, um thread que chama a sobrecarga do WaitOne evento é bloqueado até que um evento seja sinalizado. O EventWaitHandle.Set método define o estado de um evento como sinalizado.
O comportamento de um EventWaitHandle que foi sinalizado depende do seu modo de redefinição:
- Um EventWaitHandle criado com o EventResetMode.AutoReset sinalizador é redefinido automaticamente depois de liberar um único thread de espera. É como uma catraca que permite apenas um fio através de cada vez que é sinalizado. A System.Threading.AutoResetEvent classe, que deriva de EventWaitHandle, representa esse comportamento.
- Um EventWaitHandle criado com o sinalizador permanece sinalizado EventResetMode.ManualReset até que seu Reset método seja chamado. É como um portão que fica fechado até ser sinalizado e depois permanece aberto até que alguém o feche. A System.Threading.ManualResetEvent classe, que deriva de EventWaitHandle, representa esse comportamento. A System.Threading.ManualResetEventSlim classe é uma alternativa leve ao ManualResetEvent.
No Windows, você pode usar EventWaitHandle para a sincronização entre processos. Para fazer isso, crie uma EventWaitHandle instância que representa um evento de sincronização do sistema nomeado usando um dos construtores EventWaitHandle que especifica um nome ou o EventWaitHandle.OpenExisting método.
Para obter mais informações, consulte o artigo EventWaitHandle . Para obter a referência da API, consulte EventWaitHandle, AutoResetEvent, ManualResetEvente ManualResetEventSlim.
Classe CountdownEvent
A System.Threading.CountdownEvent classe representa um evento que se torna definido quando sua contagem é zero. Embora CountdownEvent.CurrentCount seja maior que zero, um thread que chama CountdownEvent.Wait é bloqueado. Chamada CountdownEvent.Signal para diminuir a contagem de um evento.
Em contraste ManualResetEvent com ou ManualResetEventSlim, que você pode usar para desbloquear vários threads com um sinal de um thread, você pode usar CountdownEvent para desbloquear um ou mais threads com sinais de vários threads.
Para obter mais informações, consulte o artigo CountdownEvent e a referência da CountdownEvent API.
Classe de barreira
A System.Threading.Barrier classe representa uma barreira de execução de thread. Um thread que chama o método sinaliza Barrier.SignalAndWait que ele atingiu a barreira e espera até que outros threads participantes alcancem a barreira. Quando todos os fios participantes atingem a barreira, eles prosseguem e a barreira é redefinida e pode ser usada novamente.
Você pode usar Barrier quando um ou mais threads exigem os resultados de outros threads antes de prosseguir para a próxima fase de computação.
Para obter mais informações, consulte o artigo Barreira e a referência da Barrier API.
Classe interligada
A System.Threading.Interlocked classe fornece métodos estáticos que executam operações atômicas simples em uma variável. Essas operações atômicas incluem adição, incremento e decréscimo, troca e troca condicional que depende de uma comparação e operação de leitura de um valor inteiro de 64 bits.
Para obter mais informações, consulte a referência da Interlocked API.
Estrutura SpinWait
A System.Threading.SpinWait estrutura fornece suporte para espera baseada em rotação. Você pode querer usá-lo quando um thread tem que esperar que um evento seja sinalizado ou uma condição seja atendida, mas quando se espera que o tempo de espera real seja menor do que o tempo de espera exigido usando um identificador de espera ou bloqueando o thread de outra forma. SpinWaitUsando o , você pode especificar um curto período de tempo para girar enquanto espera e, em seguida, ceder (por exemplo, aguardando ou dormindo) somente se a condição não foi atendida no tempo especificado.
Para obter mais informações, consulte o artigo SpinWait e a referência da SpinWait API.