Semáforo - Qual é a utilidade da contagem inicial?

96

http://msdn.microsoft.com/en-us/library/system.threading.semaphoreslim.aspx

Para criar um semáforo, preciso fornecer uma contagem inicial e uma contagem máxima. MSDN afirma que uma contagem inicial é -

O número inicial de solicitações do semáforo que podem ser concedidas simultaneamente.

Embora afirme que a contagem máxima é

O número máximo de solicitações para o semáforo que podem ser concedidas simultaneamente.

Posso entender que a contagem máxima é o número máximo de threads que podem acessar um recurso simultaneamente. Mas, qual é a utilidade da contagem inicial?

Se eu criar um semáforo com uma contagem inicial de 0 e uma contagem máxima de 2, nenhum dos meus threads de threadpool será capaz de acessar o recurso. Se eu definir a contagem inicial como 1 e a contagem máxima como 2, apenas o thread do pool de threads poderá acessar o recurso. Somente quando eu defino a contagem inicial e a contagem máxima como 2, 2 threads podem acessar o recurso simultaneamente. Então, estou realmente confuso sobre o significado da contagem inicial?

SemaphoreSlim semaphoreSlim = new SemaphoreSlim(0, 2); //all threadpool threads wait
SemaphoreSlim semaphoreSlim = new SemaphoreSlim(1, 2);//only one thread has access to the resource at a time
SemaphoreSlim semaphoreSlim = new SemaphoreSlim(2, 2);//two threadpool threads can access the resource concurrently
Caixa de areia
fonte

Respostas:

83

Sim, quando o número inicial é definido como 0 - todos os threads estarão esperando enquanto você incrementa a propriedade "CurrentCount". Você pode fazer isso com Release () ou Release (Int32).

Release (...) - irá incrementar o contador do semáforo

Espere (...) - irá decrementar

Você não pode incrementar o contador (propriedade "CurrentCount") maior que a contagem máxima que você definiu na inicialização.

Por exemplo:

SemaphoreSlim^ s = gcnew SemaphoreSlim(0,2); //s->CurrentCount = 0
s->Release(2); //s->CurrentCount = 2
...

s->Wait(); //Ok. s->CurrentCount = 1
...

s->Wait(); //Ok. s->CurrentCount = 0
...

s->Wait(); //Will be blocked until any of the threads calls Release()
SVGreg
fonte
1
Seu código será melhor apresentado na resposta do que como um comentário.
ChrisF
16
LOL é provavelmente a 5ª vez que chego a esta mesma resposta porque a documentação do construtor sempre me confunde sobre quais valores definir. Saudações
BlueStrat de
73

Então, estou realmente confuso sobre o significado da contagem inicial?

Um ponto importante que pode ajudar aqui é que Waitdiminui a contagem do semáforo e a Releaseincrementa.

initialCounté o número de acessos a recursos que serão permitidos imediatamente. Ou, em outras palavras, é o número de vezes que Waitpode ser chamado sem bloquear imediatamente após o semáforo ser instanciado.

maximumCounté a contagem mais alta que o semáforo pode obter. É o número de vezes que Releasepode ser chamado sem lançar uma exceção, supondo que a initialCountcontagem seja zero. Se initialCountestiver definido com o mesmo valor que maximumCountentão, chamar Releaseimediatamente após o semáforo ser instanciado lançará uma exceção.

Brian Gideon
fonte
20
Isso é muito útil! Eu estive pensando em Semaphores backward, como em initialCount sendo o número de recursos BLOQUEADOS iniciais, não o número de recursos que estão disponíveis imediatamente. Obrigado.
Philip Tenn
5
@PhilipTenn, concordo - a documentação não é clara a esse respeito
BlueStrat
Eu concordei, eles deveriam mudar o nome da variável ou atualizar os documentos
IronHide
@Sandbox você deve aceitar esta resposta IMO, pois ela realmente explica o significado do initialCountparâmetro.
Michał Turczyn
9

Quantos threads você deseja poder acessar o recurso de uma vez? Defina sua contagem inicial para esse número. Se esse número nunca vai aumentar ao longo da vida do programa, defina sua contagem máxima para esse número também. Dessa forma, se você tiver um erro de programação em como libera o recurso, seu programa irá travar e informá-lo.

(Existem dois construtores: um que leva apenas um valor inicial e outro que adicionalmente leva a contagem máxima. Use o que for apropriado.)

Karmastan
fonte
2

Se você deseja que nenhuma thread acesse seu recurso por algum tempo, você passa a contagem inicial como 0 e quando deseja conceder acesso a todos eles logo após a criação do semáforo, você passa o valor da contagem inicial igual à contagem máxima . Por exemplo:

hSemaphore = CreateSemaphoreA(NULL, 0, MAX_COUNT, NULL) ;

//Do something here
//No threads can access your resource

ReleaseSemaphore(hSemaphore, MAX_COUNT, 0) ;

//All threads can access the resource now

Conforme citado na documentação do MSDN- "Outro uso de ReleaseSemaphore é durante a inicialização de um aplicativo. O aplicativo pode criar um semáforo com uma contagem inicial de zero. Isso define o estado do semáforo como sem sinal e bloqueia todos os threads de acesso ao recurso protegido. Quando o aplicativo terminar sua inicialização, ele usa ReleaseSemaphore para aumentar a contagem ao seu valor máximo, para permitir o acesso normal ao recurso protegido. "

Abhineet
fonte
Desculpe, eu dei a você o exemplo em C ++ embora possa esclarecer a dúvida.
Abhineet de
1

Dessa forma, quando o thread atual cria o semáforo, ele pode reivindicar alguns recursos desde o início.

ER não
fonte
Então, você quer dizer que quando eu quero que dois threads de trabalho acessem o recurso, devo alterar a contagem inicial?
Sandbox
Não. É o tópico atual que reivindica uma contagem. Se você não quiser que o thread atual reivindique qualquer acesso, passe 0 ou use a sobrecarga com um parâmetro.
Erno
-1

Os semáforos podem ser usados ​​para proteger um conjunto de recursos . Usamos pools de recursos para reutilizar coisas que são caras de criar - como conexões de banco de dados.

Portanto, a contagem inicial se refere ao número de recursos disponíveis no pool no início de algum processo. Ao ler o initialCountcódigo, você deve estar pensando em quanto esforço inicial está investindo na criação desse pool de recursos.

Estou realmente confuso sobre o significado da contagem inicial?

Initial count = Upfront cost

Como tal, dependendo do perfil de uso de seu aplicativo, este valor pode ter um efeito dramático no desempenho de seu aplicativo. Não é apenas um número arbitrário.

Você deve pensar cuidadosamente sobre o que está criando, como é caro criar e quantos você precisa imediatamente. Você deve literalmente ser capaz de representar graficamente o valor ideal para este parâmetro e provavelmente deve pensar em torná-lo configurável para que possa adaptar o desempenho do processo ao momento em que está sendo executado.

rism
fonte
-2

Como o MSDN explica na seção Comentários:

Se initialCount for menor que maximumCount, o efeito será o mesmo como se o encadeamento atual tivesse chamado WaitOne (maximumCount menos initialCount) vezes. Se você não quiser reservar nenhuma entrada para o encadeamento que cria o semáforo, use o mesmo número para maximumCount e initialCount.

Portanto, se a contagem inicial for 0 e o máximo for 2, é como se WaitOne tivesse sido chamado duas vezes pelo encadeamento principal, de modo que atingimos a capacidade (a contagem do semáforo é 0 agora) e nenhum encadeamento pode entrar no semáforo. Da mesma forma, se a contagem inicial for 1 e max for 2, WaitOnce foi chamado uma vez e apenas um thread pode entrar antes de atingirmos a capacidade novamente e assim por diante.

Se 0 for usado para a contagem inicial, sempre podemos chamar Release (2) para aumentar a contagem do semáforo para o máximo para permitir o número máximo de threads para adquirir o recurso.

Irfan
fonte