Esse uso do monitor.wait/pulse tem uma condição de corrida?
-
26-09-2019 - |
Pergunta
Eu tenho um cenário simples de produtor/consumidor, onde há apenas um único item sendo produzido/consumido. Além disso, o produtor espera que o tópico do trabalhador termine antes de continuar. Percebo que isso evita todo o ponto de leitura multithread, mas assuma que realmente precisa ser assim (:
Este código não é compilado, mas espero que você tenha a ideia:
// m_data is initially null
// This could be called by any number of producer threads simultaneously
void SetData(object foo)
{
lock(x) // Line A
{
assert(m_data == null);
m_data = foo;
Monitor.Pulse(x) // Line B
while(m_data != null)
Monitor.Wait(x) // Line C
}
}
// This is only ever called by a single worker thread
void UseData()
{
lock(x) // Line D
{
while(m_data == null)
Monitor.Wait(x) // Line E
// here, do something with m_data
m_data = null;
Monitor.Pulse(x) // Line F
}
}
Aqui está a situação que eu não tenho certeza:
Suponha que muitos threads chamem o setData () com entradas diferentes. Apenas um deles entrará na fechadura e o restante será bloqueado na linha A. Suponha que o que entrou nos conjuntos de bloqueio m_data e faz o seu caminho para a linha C.
Pergunta: A espera () na linha C permitir que outro fio na linha A obtenha a trava e substitua m_data Antes que o tópico do trabalhador chegue a ele?
Supondo que isso não aconteça, e o fio do trabalhador processa o original m_data, e, eventualmente, vá para a linha F, o que acontece quando esse pulso () dispara?
Somente o thread aguardando na linha C poderá obter a fechadura? Ou estará competindo com todos os outros threads esperando na linha A também?
Essencialmente, quero saber se Pulse ()/Wait () se comunica especialmente "sob o capô" ou se eles estão no mesmo nível com Lock ().
A solução para esses problemas, se houver, é óbvia, é claro - apenas Surround setData () com outra trava - digamos, bloqueio (y). Estou curioso para saber se é um problema para começar.
Solução
Não há garantia de que o consumidor seja inserido na fila de espera ou pronta diante de outro produtor.
Os monitores de estilo C# e Java são descritos em Wikipedia, em "Monitores de Condição implícita".
Uma boa visão geral do que acontece em Monitor
(Tirado de isto excelente site):
"A espera () na linha C permitir que outro fio na linha A obtenha a fechadura e substitua M_Data antes mesmo de o tópico do trabalhador chegar?"
Suponha que SetData()
é chamado por dois threads de produtores, P1 & P2.
Um tópico de consumo, C1 é iniciado também.
P1, P2 e C1 todos entram na fila pronta.
P1 adquire a fechadura primeiro.
A fila de espera está vazia, Pulse()
sobre line B
não tem efeito.
P1 espera line C
, então é colocado na fila de espera.
O próximo thread na fila Ready adquire a fechadura.
Pode ser igualmente P2 ou C1 - No primeiro caso, a afirmação falha.
Você tem uma condição de corrida.
"Supondo que isso não aconteça, e o fio do trabalhador processa o M_Data original e, eventualmente, segue para a linha F, o que acontece quando esse pulso () dispara?"
Ele moverá um garçom da fila de espera para a fila pronta.
A fechadura é mantida pelo tópico que emite o Pulse()
.
O fio notificado vai ter uma chance Para adquirir a fechadura após o lixo pulsante, a trava (já pode haver outros na fila pronta).
A partir de Msdn, monitor.pulse ():
"O segmento que atualmente possui a trava no objeto especificado chama esse método para sinalizar o próximo fio na fila para a trava. Ao receber o pulso, o fio de espera é movido para a fila pronta. Quando o fio que invocou o pulso libera a fechadura da fechadura , o próximo tópico na fila pronta (que não é necessariamente o segmento que foi pulsado) adquire a trava ".
"Somente o fio aguardando na linha C poderá conseguir a fechadura? Ou estará competindo com todos os outros threads esperando na linha A também?"
Todos os threads que estão na fila pronta "competem" por ter a trava a seguir.
Não importa se eles chegaram lá direto ou da fila de espera por meio de Pulse()
.
As "filas" podem ser implementadas por outros meios. (Não o fila estrutura de dados como tal).
Assim a Monitor
A implementação pode não garantir justiça - mas pode ter maior taxa de transferência/desempenho geral.