O que é contenção de thread?

120

Alguém pode explicar simplesmente o que é contenção de thread?

Eu pesquisei, mas não consigo encontrar uma explicação simples.

Tony o Leão
fonte
9
Então, escreva qual é o seu pensamento vago sobre ele, para que possamos ver onde você está ou seu entendimento pode estar correto.
James Black

Respostas:

87

Essencialmente, a contenção de thread é uma condição em que uma thread está esperando por um bloqueio / objeto que está sendo mantido por outra thread. Portanto, esse thread em espera não pode usar esse objeto até que o outro thread desbloqueie esse objeto específico.

teclado P
fonte
53
Esta resposta está incompleta (como a maioria das outras). Embora uma trava seja um tipo de coisa sobre a qual pode haver contenção, está longe de ser a única coisa. Também pode haver contenção para recursos sem bloqueio. (Por exemplo, se dois encadeamentos continuarem incrementando atomicamente o mesmo número inteiro, eles podem enfrentar contenção devido ao ping-pong do cache. Nenhum bloqueio está envolvido.)
David Schwartz
No caso de um Global Interpreter Lock (GIL), como no CPython, onde um thread deve sempre adquirir o GIL, vários threads em execução no mesmo processo estão, portanto, em contenção por padrão.
Acumenus de
Acho que você explicou em termos de Deadlock, mas é muito diferente do Deadlock.
Harshit Gupta
185

Várias respostas parecem se concentrar na contenção de bloqueio, mas os bloqueios não são os únicos recursos nos quais a contenção pode ser experimentada. A contenção é simplesmente quando dois encadeamentos tentam acessar o mesmo recurso ou recursos relacionados de tal forma que pelo menos um dos encadeamentos em disputa é executado mais lentamente do que se os outros encadeamentos não estivessem em execução.

O exemplo mais óbvio de contenção está em uma fechadura. Se o encadeamento A tiver um bloqueio e o encadeamento B quiser adquirir esse mesmo bloqueio, o encadeamento B terá que esperar até que o encadeamento A libere o bloqueio.

Agora, isso é específico da plataforma, mas o encadeamento pode sofrer lentidão mesmo se nunca tiver que esperar que o outro encadeamento libere o bloqueio! Isso ocorre porque um bloqueio protege alguns tipos de dados, e os próprios dados também serão contestados.

Por exemplo, considere um thread que adquire um bloqueio, modifica um objeto, libera o bloqueio e faz algumas outras coisas. Se dois encadeamentos estiverem fazendo isso, mesmo que nunca lutem pelo bloqueio, os encadeamentos podem ser executados muito mais lentamente do que se apenas um deles estivesse em execução.

Por quê? Digamos que cada thread esteja executando em seu próprio núcleo em uma CPU x86 moderna e os núcleos não compartilham um cache L2. Com apenas um thread, o objeto pode permanecer no cache L2 a maior parte do tempo. Com os dois threads em execução, cada vez que um thread modifica o objeto, o outro thread descobrirá que os dados não estão em seu cache L2 porque a outra CPU invalidou a linha do cache. Em um Pentium D, por exemplo, isso fará com que o código seja executado na velocidade FSB, que é muito menor do que a velocidade do cache L2.

Uma vez que a contenção pode ocorrer mesmo que o bloqueio não seja disputado, a contenção também pode ocorrer quando não há bloqueio. Por exemplo, digamos que sua CPU suporte um incremento atômico de uma variável de 32 bits. Se um thread continua aumentando e diminuindo uma variável, a variável ficará quente no cache a maior parte do tempo. Se dois encadeamentos o fizerem, seus caches disputarão a propriedade da memória que contém essa variável, e muitos acessos serão mais lentos, pois o protocolo de coerência do cache opera para garantir a propriedade de cada núcleo da linha do cache.

Ironicamente, os bloqueios normalmente reduzem a contenção. Por quê? Porque sem um bloqueio, dois threads podem operar no mesmo objeto ou coleção e causar muita contenção (por exemplo, existem filas livres de bloqueio). Os bloqueios tendem a cancelar a programação de encadeamentos concorrentes, permitindo que encadeamentos não concorrentes sejam executados. Se o encadeamento A mantém um bloqueio e o encadeamento B deseja esse mesmo bloqueio, a implementação pode executar o encadeamento C em vez disso. Se o thread C não precisar desse bloqueio, a contenção futura entre os threads A e B pode ser evitada por algum tempo. (Claro, isso assume que há outros threads que podem ser executados. Não ajudará se a única maneira de o sistema como um todo fazer um progresso útil for executando threads que contenham.)

David Schwartz
fonte
4
+1 Além disso, só para tornar isso explícito, as duas variáveis ​​pelas quais dois núcleos estão lutando não precisam ser a mesma variável para causar contenção, elas só precisam ser armazenadas na memória na mesma linha de cache. Estruturas de enchimento e / ou estruturas de alinhamento com a memória podem ajudar a evitar essa forma de contenção.
Rob_before_edits
1
@David, por favor, ajude a entender o último parágrafo de sua resposta com mais detalhes
Aluno
4
@Naroji Faça uma pergunta sobre isso.
David Schwartz
@DavidSchwartz, você é um programador C?
Pacerier
@Pacerier C ++ principalmente.
David Schwartz
19

A partir daqui :

Uma contenção ocorre quando um encadeamento está aguardando um recurso que não está prontamente disponível; ele retarda a execução de seu código, mas pode limpar com o tempo.

Um conflito ocorre quando um encadeamento está aguardando um recurso que um segundo encadeamento bloqueou e o segundo encadeamento está aguardando um recurso que o primeiro encadeamento bloqueou. Mais de dois threads podem estar envolvidos em um deadlock. Um impasse nunca se resolve. Freqüentemente, faz com que todo o aplicativo, ou a parte que está passando pelo deadlock, seja interrompida.

Jon B
fonte
Isso também explica a diferença entre o tópico Contention e Deadlock
Sankalp
3

Acho que deve haver algum esclarecimento do OP sobre o contexto da pergunta - posso pensar em 2 respostas (embora tenha certeza de que há acréscimos a esta lista):

  1. se você está se referindo ao "conceito" geral de contenção de encadeamentos e como ele pode se apresentar em um aplicativo, adio a resposta detalhada de @DavidSchwartz acima.

  2. Há também o contador de desempenho '.NET CLR Locks and Threads: Total # of Contentions'. Conforme retirado da descrição do PerfMon para este contador, ele é definido como:

    Este contador exibe o número total de vezes que os threads no CLR tentaram adquirir um bloqueio gerenciado sem sucesso. Os bloqueios gerenciados podem ser adquiridos de várias maneiras; pela instrução "lock" em C # ou chamando System.Monitor.Enter ou usando o atributo personalizado MethodImplOptions.Synchronized.

... e tenho certeza que outros para outros sistemas operacionais e estruturas de aplicativos.

Dave Black
fonte
2

Você tem 2 tópicos. Thread A e Thread B, você também tem o objeto C.

A está atualmente acessando o objeto C e colocou um bloqueio nesse objeto. B precisa acessar o objeto C, mas não pode fazer isso até que A libere o bloqueio no objeto C.

Aaron M
fonte
1

Outra palavra pode ser simultaneidade. É simplesmente a ideia de dois ou mais threads tentando usar o mesmo recurso.

Mark Wilkins
fonte
1

Para mim, a contenção é uma competição entre 2 ou mais threads em um recurso compartilhado. O recurso pode ser uma fechadura, um contador, etc. Competição significa "quem o obtém primeiro". Quanto mais tópicos, mais contenção. Quanto mais frequente o acesso a um recurso, mais contenção.

Maciej
fonte
1

Imagine o seguinte cenário. Você está se preparando para o exame final de amanhã e está com um pouco de fome. Então, você dá a seu irmão mais novo dez dólares e pede a ele para comprar uma pizza para você. Nesse caso, você é o thread principal e seu irmão é um thread filho. Assim que seu pedido for feito, você e seu irmão estarão fazendo o trabalho ao mesmo tempo (isto é, estudar e comprar uma pizza). Agora, temos dois casos a considerar. Primeiro, seu irmão traz sua pizza de volta e termina enquanto você está estudando. Nesse caso, você pode parar de estudar e curtir a pizza. Em segundo lugar, você termina seu estudo cedo e dorme (ou seja, seu trabalho designado para hoje - estudar para o exame final de amanhã - está feito) antes que a pizza esteja disponível. Claro, você não consegue dormir; caso contrário, você não terá a chance de comer a pizza.

Como no exemplo, os dois casos dão sentido de rivalidade.

snr
fonte
0

A contenção de thread também é afetada por operações de E / S. Exemplo quando um Thread aguardando a leitura do arquivo pode ser considerado uma contenção. Use portas de conclusão de E / S como solução.

amilamad
fonte
0

A contenção de bloqueio ocorre quando um thread tenta adquirir o bloqueio para um objeto que já foi adquirido por outro thread *. Até que o objeto seja liberado, o thread é bloqueado (em outras palavras, está no estado Esperando). Em alguns casos, isso pode levar a uma chamada execução serial que afeta negativamente a aplicação.

da documentação dotTrace

AndreyT
fonte