Aqui está o código que tenho, mas não entendo o que SemaphoreSlim
está fazendo.
async Task WorkerMainAsync()
{
SemaphoreSlim ss = new SemaphoreSlim(10);
List<Task> trackedTasks = new List<Task>();
while (DoMore())
{
await ss.WaitAsync();
trackedTasks.Add(Task.Run(() =>
{
DoPollingThenWorkAsync();
ss.Release();
}));
}
await Task.WhenAll(trackedTasks);
}
void DoPollingThenWorkAsync()
{
var msg = Poll();
if (msg != null)
{
Thread.Sleep(2000); // process the long running CPU-bound job
}
}
O que espera ss.WaitAsync();
e ss.Release();
faz?
Eu acho que se eu executar 50 threads de uma vez, então escrevo o código, SemaphoreSlim ss = new SemaphoreSlim(10);
então ele será forçado a executar 10 threads ativas por vez.
Quando um dos 10 tópicos for concluído, outro será iniciado. Se eu não estiver certo, então me ajude a entender com uma situação de amostra.
Por que é await
necessário junto com ss.WaitAsync();
? O que ss.WaitAsync();
fazer?
Respostas:
Está correto; o uso do semáforo garante que não haverá mais de 10 trabalhadores fazendo este trabalho ao mesmo tempo.
Chamar
WaitAsync
o semáforo produz uma tarefa que será concluída quando o thread tiver "acesso" ao token.await
-ing essa tarefa permite que o programa continue a execução quando for "permitido" fazê-lo. Ter uma versão assíncrona, em vez de chamarWait
, é importante tanto para garantir que o método permaneça assíncrono, em vez de síncrono, quanto para lidar com o fato de que umasync
método pode estar executando código em vários threads, devido aos retornos de chamada e assim a afinidade natural do fio com semáforos pode ser um problema.Uma nota lateral:
DoPollingThenWorkAsync
não deve ter oAsync
postfix porque não é realmente assíncrono, é síncrono. Basta ligarDoPollingThenWork
. Isso reduzirá a confusão para os leitores.fonte
Thread.Sleep
no código original devastaria o agendador de tarefas. Se você não é assíncrono com o núcleo, você não é assíncrono.No jardim de infância ao virar da esquina, eles usam um SemaphoreSlim para controlar quantas crianças podem brincar na sala de educação física.
Pintaram no chão, fora da sala, 5 pares de pegadas.
Conforme as crianças chegam, elas deixam seus sapatos em um par de pegadas livres e entram na sala.
Assim que terminarem de jogar, eles saem, pegam seus sapatos e "liberam" uma vaga para outra criança.
Se uma criança chega e não há pegadas restantes, ela vai brincar em outro lugar ou apenas fica por um tempo e verifica de vez em quando (ou seja, sem prioridades FIFO).
Quando uma professora está por perto, ela "libera" uma fileira extra de 5 pegadas do outro lado do corredor, de modo que mais 5 crianças possam brincar na sala ao mesmo tempo.
Ele também tem as mesmas "armadilhas" do SemaphoreSlim ...
Se uma criança termina de jogar e sai da sala sem recolher os sapatos (não aciona o "desbloqueio"), a vaga permanece bloqueada, mesmo que teoricamente haja uma vaga vazia. O garoto geralmente é repreendido.
Às vezes, uma ou duas crianças sorrateiras escondem seus sapatos em outro lugar e entram na sala, mesmo que todas as pegadas já tenham sido tiradas (ou seja, o SemaphoreSlim não controla "realmente" quantas crianças estão na sala).
Isso geralmente não termina bem, já que a superlotação da sala tende a acabar com o choro das crianças e o professor fechando totalmente a sala.
fonte
Embora eu aceite que essa pergunta realmente se relaciona a um cenário de bloqueio de contagem regressiva, achei que valeria a pena compartilhar este link que descobri para aqueles que desejam usar um SemaphoreSlim como um bloqueio assíncrono simples. Ele permite que você use a instrução using, que pode tornar a codificação mais organizada e segura.
http://www.tomdupont.net/2016/03/how-to-release-semaphore-with-using.html
Eu fiz troca
_isDisposed=true
e_semaphore.Release()
em torno de sua Descarte embora no caso de alguma forma foi chamado várias vezes.Também é importante observar que SemaphoreSlim não é um bloqueio reentrante, o que significa que se o mesmo encadeamento chamar WaitAsync várias vezes, a contagem do semáforo será diminuída todas as vezes. Em suma, SemaphoreSlim não reconhece Thread.
Com relação às questões de qualidade de código, é melhor colocar o Release dentro de uma tentativa para garantir que ele sempre seja lançado.
fonte