Explicação do texto sobre rosqueamento em “C# 3.0 em poucas palavras”
-
22-09-2019 - |
Pergunta
Enquanto lê C# 3.0 em poucas palavras por Joseph e Ben Albahari, Me deparei com o parágrafo seguinte (página 673, primeiro parágrafo na seção intitulado "Sinalização com espera e pulso")
"O Monitor A classe fornece outra construção de sinalização por meio de dois métodos estáticos, Espere e Pulso. O princípio é que você escreve a lógica de sinalização usando sinalizadores e campos personalizados (fechados em trancar declarações) e depois apresentar Espere e Pulso comandos para mitigar a fiação da CPU. A vantagem dessa abordagem de baixo nível é que apenas com Espere, Pulso, e as trancar declaração, você pode alcançar a funcionalidade de AUTORESETEVENT, ManualResetevent, e Semáforo, assim como WaitHandleMétodos estáticos Espera e Espera. Além disso, Espere e Pulsopode ser passível de passageira em situações em que todas as alças de espera são desafiadas parcimoniosamente ".
Minha pergunta é: qual é a interpretação correta do último frase?
- Uma situação com um número decente/grande de alças de espera, onde o Wagone () é apenas ocasionalmente chamado em qualquer alça de espera específica.
- Uma situação com um número decente/grande de alças de espera, onde raramente faz mais de um fio tende a bloquear em qualquer alça de espera específica.
- Alguma outra interpretação.
Também apreciaria exemplos esclarecedores de tais situações e talvez como e/ou por que eles são tratados com mais eficiência por meio de espera e pulso, e não por outros métodos.
Obrigada!
EDIT: Encontrei o texto online aqui
Solução
O que isso está dizendo é que existem algumas situações em que a espera e o pulso fornecem uma solução mais simples do que as alças de espera. Em geral, isso acontece onde:
- O garçom, em vez do notificador, decide quando desbloquear
- A condição de bloqueio envolve mais do que uma bandeira simples (talvez várias variáveis)
Você ainda pode usar alças de espera nessas situações, mas o Wait/Pulse tende a ser mais simples. O melhor de Wait/Pulse é que a espera libera a fechadura subjacente enquanto espera. Por exemplo, no exemplo a seguir, estamos lendo _x e _y na segurança de um bloqueio - e, no entanto, esse bloqueio é liberado enquanto espera para que outro tópico possa atualizar essas variáveis:
lock (_locker)
{
while (_x < 10 && _y < 20) Monitor.Wait (_locker);
}
Outro tópico pode atualizar _x e _y atomicamente (em virtude da trava) e depois pulsar para sinalizar o garçom:
lock (_locker)
{
_x = 20;
_y = 30;
Monitor.Pulse (_locker);
}
A desvantagem da espera/pulso é que é mais fácil errar e cometer um erro (por exemplo, atualizando uma variável e esquecendo de pulsar). Em situações em que um programa com alças de espera é igualmente simples para um programa com espera/pulso, eu recomendo ir com alças de espera por esse motivo.
Em termos de eficiência/consumo de recursos (que eu acho que você estava aludindo), o Wait/Pulse geralmente é mais rápido e mais leve (pois possui uma implementação gerenciada). Isso raramente é um grande negócio na prática, no entanto. E nesse ponto, o Framework 4.0 inclui versões gerenciadas de baixa sobrecarga de manualresetevent e semáforo (manualreteventslim e semáforeslim).
O Framework 4.0 também fornece muito mais opções de sincronização que diminuem a necessidade de espera/pulso:
- CountdownEvent
- Barreira
- Paralelismo de plinq / dados (asparalelos, paralelos.invoke, paralelo. para, paralelo.foreach)
- Tarefas e continuações
Tudo isso é muito mais alto que o Wait/Pulse e a IMO é preferível para escrever código confiável e sustentável (supondo que eles resolvam a tarefa em questão).