Tenho feito experiências com multi-threading e processamento paralelo e precisava de um contador para fazer algumas contagens básicas e análises estatísticas da velocidade do processamento. Para evitar problemas com o uso simultâneo de minha classe, usei uma instrução de bloqueio em uma variável privada em minha classe:
private object mutex = new object();
public void Count(int amount)
{
lock(mutex)
{
done += amount;
}
}
Mas eu estava me perguntando ... quão caro é o bloqueio de uma variável? Quais são os efeitos negativos no desempenho?
c#
.net
multithreading
locking
parallel-processing
Kees C. Bakker
fonte
fonte
Respostas:
Aqui está um artigo que analisa o custo. A resposta curta é 50 ns.
fonte
lock
mais do que você se preocupar com o custo de usar uma variável.A resposta técnica é que isso é impossível de quantificar, pois depende muito do estado dos buffers de write-back da memória da CPU e de quantos dados que o pré-buscador reuniu devem ser descartados e relidos. Ambos são muito não determinísticos. Eu uso 150 ciclos de CPU como uma aproximação final que evita grandes decepções.
A resposta prática é que é muuuuito mais barato do que a quantidade de tempo que você gastará depurando seu código quando achar que pode pular um bloqueio.
Para obter um número rígido, você terá que medir. O Visual Studio tem um analisador de simultaneidade inteligente disponível como uma extensão.
fonte
Leitura adicional:
Gostaria de apresentar alguns artigos meus, que estão interessados em primitivas de sincronização gerais e estão se aprofundando no Monitor, comportamento da instrução de bloqueio C #, propriedades e custos, dependendo de cenários distintos e número de threads. Ele está especificamente interessado em desperdício de CPU e períodos de capacidade para entender quanto trabalho pode ser realizado em vários cenários:
https://www.codeproject.com/Articles/1236238/Unified-Concurrency-I-Introduction https://www.codeproject.com/Articles/1237518/Unified-Concurrency-II-benchmarking-methodologies https: // www. codeproject.com/Articles/1242156/Unified-Concurrency-III-cross-benchmarking
Resposta original:
Oh céus!
Parece que a resposta correta sinalizada aqui como A RESPOSTA é inerentemente incorreta! Gostaria de pedir ao autor da resposta, respeitosamente, que leia o link do artigo até o final. artigo
O autor do artigo, de 2003 artigo foi medição em única máquina Dual Core e, no primeiro caso de medição, ele medido bloqueio com apenas um único segmento eo resultado foi de cerca de 50 ns por acesso de bloqueio.
Não diz nada sobre um bloqueio no ambiente simultâneo. Portanto, temos que continuar lendo o artigo e na segunda metade, o autor estava medindo o cenário de bloqueio com dois e três threads, que se aproxima dos níveis de simultaneidade dos processadores de hoje.
Então o autor diz, que com dois threads no Dual Core, os bloqueios custam 120ns, e com 3 threads vai para 180ns. Portanto, parece ser claramente dependente do número de threads acessando o bloqueio simultaneamente.
Portanto, é simples, não é 50 ns a menos que seja um único thread, onde o bloqueio fica inútil.
Outra questão a considerar é que é medido como o tempo médio !
Se o tempo das iterações fosse medido, haveria tempos pares entre 1ms a 20ms, simplesmente porque a maioria foi rápida, mas poucos encadeamentos estarão esperando pelo tempo dos processadores e incorrerão em atrasos de até milissegundos.
Isso é uma má notícia para qualquer tipo de aplicativo que requeira alto rendimento e baixa latência.
E a última questão a ser considerada é que pode haver operações mais lentas dentro da fechadura e, muitas vezes, é esse o caso. Quanto mais tempo o bloco de código for executado dentro da fechadura, maior será a contenção e os atrasos serão muito altos.
Considere que já se passou mais de uma década desde 2003, ou seja, poucas gerações de processadores projetados especificamente para funcionar totalmente simultaneamente e o bloqueio está prejudicando consideravelmente seu desempenho.
fonte
Isso não responde à sua consulta sobre desempenho, mas posso dizer que o .NET Framework oferece um
Interlocked.Add
método que permitirá que você adicione seuamount
ao seudone
membro sem bloquear manualmente em outro objeto.fonte
lock
(Monitor.Enter / Exit) é muito barato, mais barato do que alternativas como Waithandle ou Mutex.Mas e se fosse (um pouco) lento, você preferisse um programa rápido com resultados incorretos?
fonte
O custo de um bloqueio em um loop apertado, em comparação com uma alternativa sem bloqueio, é enorme. Você pode fazer loops muitas vezes e ainda ser mais eficiente do que uma fechadura. É por isso que as filas sem bloqueio são tão eficientes.
Resultado:
fonte
Existem algumas maneiras diferentes de definir "custo". Existe a sobrecarga real de obter e liberar o bloqueio; como Jake escreve, isso é insignificante, a menos que essa operação seja executada milhões de vezes.
Mais relevante é o efeito que isso tem no fluxo de execução. Este código só pode ser inserido por um tópico de cada vez. Se você tiver 5 threads realizando essa operação regularmente, 4 deles vão acabar esperando que o bloqueio seja liberado e, então, será o primeiro thread agendado para inserir aquele trecho de código depois que o bloqueio for liberado. Portanto, seu algoritmo sofrerá significativamente. O quanto isso depende do algoritmo e da frequência com que a operação é chamada. Você realmente não pode evitá-la sem introduzir condições de corrida, mas pode melhorá-la minimizando o número de chamadas para o código bloqueado.
fonte