Alguém pode explicar simplesmente o que é contenção de thread?
Eu pesquisei, mas não consigo encontrar uma explicação simples.
Alguém pode explicar simplesmente o que é contenção de thread?
Eu pesquisei, mas não consigo encontrar uma explicação simples.
Respostas:
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.
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.)
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.
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):
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.
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.
Outra palavra pode ser simultaneidade. É simplesmente a ideia de dois ou mais threads tentando usar o mesmo recurso.
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.
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.
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.