Entendo o básico sobre o que são corridas de dados e como bloqueios / mutexes / semáforos ajudam a evitá-las. Mas o que acontece se você tiver uma "condição de corrida" na própria fechadura? Por exemplo, dois threads diferentes, talvez no mesmo aplicativo, mas executando em processadores diferentes, tentam adquirir um bloqueio exatamente ao mesmo tempo .
O que acontece depois? O que é feito para evitar isso? É impossível, ou simplesmente improvável? Ou é uma condição real de corrida esperando para acontecer?
multithreading
concurrency
locks
Gavin Howard
fonte
fonte
Respostas:
Impossível. Ele pode ser implementado de diferentes maneiras, por exemplo, através do Compare-and-swap, onde o hardware garante execução sequencial. Pode ficar um pouco complicado na presença de múltiplos núcleos ou até vários soquetes e precisa de um protocolo complicado entre os núcleos, mas tudo isso é resolvido.
fonte
Estude o conceito de operações atômicas de "Teste e Configuração".
Essencialmente, a operação não pode ser dividida - não é possível fazer duas coisas exatamente ao mesmo tempo. Ele verificará um valor, configurará se estiver claro e retornará o valor como estava no teste. Em uma operação de bloqueio, o resultado será sempre "lock == TRUE" após um teste e ajuste, a única diferença é que ele foi definido ou não no início.
No nível do microcódigo em um processador de núcleo único, esta é uma instrução indivisível e fácil de implementar. Com processadores múltiplos e multicore, fica mais difícil, mas como programadores não precisamos nos preocupar com isso, pois ele foi projetado para funcionar por pessoas realmente inteligentes que fazem o silício. Essencialmente, eles fazem a mesma coisa - fazem uma instrução atômica que é uma versão sofisticada do teste e configuração
fonte
Basta colocar o código para entrar na seção crítica, especialmente projetado para que uma condição de corrida não viole a exclusão mútua.
Na maioria das vezes, são usados loops atômicos de comparação e configuração que são executados no nível do hardware
Na ausência disso, existem soluções de software bem estudadas para permitir a exclusão mútua.
fonte
Não é possível que dois (ou mais) threads adquiram bloqueio ao mesmo tempo. Existem alguns tipos de métodos de sincronização, por exemplo:
Espera ativa - bloqueio de rotação
Pseudo-código:
XCHG é um exemplo de operação atômica (existe na arquitetura x86) que primeiro define novo valor para uma variável "lock" e depois retorna o valor antigo. Atômico significa que não pode ser interrompido - no exemplo acima, entre definir novo valor e retornar antigo. Resultado atômico - determinístico, não importa o quê.
Quando o bloqueio é igual a 0, outro thread pode entrar na seção crítica - enquanto o loop termina.
Suspensão de encadeamento - por exemplo, contagem de semáforo
Existe dois operação atômica
.Wait()
e.Signal()
e temos inteiro variável vamos chamá-loint currentValue
.Agora, resolver o problema crítico da seção é realmente fácil:
Pseudo-código:
Geralmente, a API do encadeamento de programação deve permitir que você especifique o máximo de encadeamentos simultâneos na seção crítica de semáforo. Obviamente, existem mais tipos de sincronização em sistemas multithread (mutex, monitores, semáforo binário etc.), mas eles se baseiam nas idéias acima. Alguém poderia argumentar que os métodos que usam a suspensão de encadeamento devem ser preferidos à espera ativa (para que a CPU não seja desperdiçada) - nem sempre é a verdade. Quando o encadeamento está sendo suspenso, ocorre uma operação cara chamada troca de contexto. No entanto, é razoável quando o tempo de espera é curto (número de threads ~ número de núcleos).
fonte