Partilhar via


Semáforo e SemaphoreSlim

A System.Threading.Semaphore classe representa um semáforo nomeado (em todo o sistema) ou local. É uma capa fina à volta do objeto semáforo Win32. Os semáforos do Win32 são semáforos de contagem que controlam o acesso a um grupo 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 curtos. Durante a fase de spin-wait, a CPU está ativamente a girar — não está inativa. Quão curta "curta" precisa de ser depende da natureza da espera: se as threads estiverem a competir por recursos da CPU, a thread em rotação está a consumir tempo de CPU, por isso a espera deve ser muito curta, medida em microssegundos. Se a espera for para um recurso não dependente da CPU (como I/O), a sobrecarga de spin-wait é menos preocupante e a espera aceitável pode ser um pouco mais longa, medida em milissegundos. Quando os tempos de espera são imprevisíveis ou se espera que sejam significativos, use System.Threading.Semaphore em vez disso, que não gira. SemaphoreSlim depende tanto quanto possível de primitivos de sincronização fornecidos pelo Common Language Runtime (CLR). No entanto, também fornece controlos de espera baseados em kernel, inicializados de forma preguiçosa, sempre que necessário para permitir a espera em múltiplos semáforos. SemaphoreSlim Também suporta o uso de tokens de cancelamento, mas não suporta semáforos nomeados nem o uso de um handle de espera para sincronização.

Gerir um recurso limitado

As threads entram no semáforo ao chamar diferentes métodos, dependendo do tipo de semáforo. Para um System.Threading.Semaphore objeto, chame-se o WaitOne método (herdado de WaitHandle). Para um objeto SemaphoreSlim, chamar o método SemaphoreSlim.Wait ou SemaphoreSlim.WaitAsync. Quando a chamada retorna, a contagem no semáforo é reduzida. Quando um thread solicita entrada e a contagem é zero, o thread é bloqueado. À medida que os threads libertam o semáforo ao chamar o método Semaphore.Release ou SemaphoreSlim.Release, os threads bloqueados podem entrar. Nenhuma ordem garantida — como primeiro a entrar, primeiro a sair (FIFO) ou último a entrar, primeiro a sair (LIFO) — determina qual thread bloqueado entra no semáforo a seguir.

Uma thread pode entrar no semáforo várias vezes chamando o método do objeto System.Threading.Semaphore ou o método do objeto WaitOne repetidamente. Para libertar o semáforo, chame o método Semaphore.Release() ou SemaphoreSlim.Release() o mesmo número de vezes que o thread entrou. Alternativamente, chamar o Semaphore.Release(Int32) ou SemaphoreSlim.Release(Int32) overload e especificar o número de entradas a libertar.

Semáforos e identidade de fios

Os dois tipos de semáforo não impõem a identidade de thread nas chamadas para os métodos WaitOne, Wait, Release e SemaphoreSlim.Release. Por exemplo, um cenário de uso comum para semáforos envolve um thread produtor e um thread consumidor, com um thread sempre incrementando a contagem do semáforo e o outro sempre decrementando-a.

Certifica-te de que uma thread não liberta o semáforo demasiadas 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 é abrangente ao sistema — uma vez criado, é visível para todos os threads em todos os processos. Semáforos nomeados podem, portanto, sincronizar as atividades dos processos e dos threads.

Crie um Semaphore objeto que represente um semáforo de sistema nomeado usando um dos construtores que especifica um nome.

Importante

Como os semáforos nomeados são de todo o sistema, é possível ter múltiplos Semaphore objetos que representem o mesmo semáforo com nome. 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. Como são abrangentes a nível do sistema, outro processo com o mesmo nome pode entrar no seu sinalizador de forma inesperada. 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. Também pode aplicar segurança de controlo 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 está protegido. Proteger os semáforos com segurança de controlo de acesso ajuda a prevenir ataques maliciosos, mas não resolve o problema das colisões não intencionais de nomes.

Ver também