Semáforo e SemaphoreSlim
A System.Threading.Semaphore classe representa um semáforo nomeado (em todo o sistema) ou local. É um invólucro fino em torno do objeto semáforo Win32. Os semáforos do Win32 estão contando semáforos, que podem ser usados para controlar o acesso a um pool de recursos.
A SemaphoreSlim classe representa um semáforo leve e rápido que pode ser usado para esperar dentro de um único processo quando se espera que os tempos de espera sejam muito curtos. SemaphoreSlim depende tanto quanto possível de primitivos de sincronização fornecidos pelo Common Language Runtime (CLR). No entanto, ele também fornece alças de espera baseadas em kernel inicializadas preguiçosamente, conforme necessário para suportar a espera em vários semáforos. SemaphoreSlim também suporta o uso de tokens de cancelamento, mas não suporta semáforos nomeados ou o uso de um identificador de espera para sincronização.
Gerenciando um recurso limitado
Os threads entram no semáforo chamando o WaitOne método, que é herdado da WaitHandle classe, no caso de um System.Threading.Semaphore objeto, ou o SemaphoreSlim.WaitSemaphoreSlim.WaitAsync ou método, no caso de um SemaphoreSlim objeto. Quando a chamada retorna, a contagem no semáforo é diminuída. Quando um thread solicita entrada e a contagem é zero, o thread é bloqueado. À medida que os threads liberam o semáforo chamando o método orSemaphoreSlim.Release, os Semaphore.Release threads bloqueados podem entrar. Não há nenhuma ordem garantida, como first-in, first-out (FIFO) ou last-in, first-out (LIFO), para threads bloqueados entrarem no semáforo.
Um thread pode entrar no semáforo várias vezes chamando o System.Threading.Semaphore método do WaitOne objeto ou o SemaphoreSlim método do Wait objeto repetidamente. Para liberar o semáforo, o thread pode chamar a Semaphore.Release() sobrecarga do método ou SemaphoreSlim.Release() o mesmo número de vezes, ou chamar a Semaphore.Release(Int32) sobrecarga do método ou SemaphoreSlim.Release(Int32) e especificar o número de entradas a serem liberadas.
Semáforos e Identidade do Fio
Os dois tipos de semáforo não impõem a identidade de thread em chamadas para os WaitOnemétodos , Wait, Releasee SemaphoreSlim.Release . Por exemplo, um cenário de uso comum para semáforos envolve um fio produtor e um fio consumidor, com um segmento sempre incrementando a contagem de semáforos e o outro sempre diminuindo-a.
É responsabilidade do programador garantir que um thread não libere o semáforo muitas vezes. Por exemplo, suponha que um semáforo tenha uma contagem máxima de dois, e que o fio A e o fio B entrem no semáforo. Se um erro de programação no thread B fizer com que ele chame Release
duas vezes, ambas as chamadas serão bem-sucedidas. A contagem do semáforo está cheia, e quando o fio A eventualmente chama Release
, um SemaphoreFullException é lançado.
Semáforos Nomeados
O sistema operacional Windows permite que os semáforos tenham nomes. Um semáforo nomeado é em todo o sistema. Ou seja, uma vez que o semáforo nomeado é criado, ele é visível para todos os threads em todos os processos. Assim, o semáforo nomeado pode ser usado para sincronizar as atividades de processos, bem como threads.
Você pode criar um Semaphore objeto que representa um semáforo de sistema nomeado usando um dos construtores que especifica um nome.
Nota
Como os semáforos nomeados são em todo o sistema, é possível ter vários Semaphore objetos que representam o mesmo semáforo nomeado. Cada vez que você chama um construtor ou o Semaphore.OpenExisting método, um novo Semaphore objeto é criado. Especificar o mesmo nome repetidamente cria vários objetos que representam o mesmo semáforo nomeado.
Tenha cuidado ao usar semáforos nomeados. Por serem em todo o sistema, outro processo que usa o mesmo nome pode entrar no seu semáforo inesperadamente. Um código mal-intencionado executado no mesmo computador pode usar isso como base de um ataque de negação de serviço.
Use a segurança de controle de acesso para proteger um Semaphore objeto que representa um semáforo nomeado, de preferência usando um construtor que especifica um System.Security.AccessControl.SemaphoreSecurity objeto. Você também pode aplicar segurança de controle de acesso usando o Semaphore.SetAccessControl método, mas isso deixa uma janela de vulnerabilidade entre o momento em que o semáforo é criado e o momento em que é protegido. Proteger semáforos com segurança de controle de acesso ajuda a evitar ataques mal-intencionados, mas não resolve o problema de colisões não intencionais de nomes.