Pergunta

Hoje me deparei com esta pergunta:

você tem um código

static int counter = 0;
void worker() {
    for (int i = 1; i <= 10; i++)
        counter++;
}

Se worker seria chamado a partir de duas linhas diferentes, o valor será counter ter após ambos são acabado?

Eu sei que na verdade poderia ser qualquer coisa. Mas minhas entranhas internas me diz, que counter++ provavelmente será traduzido para instrução assembler única, e se ambos os segmentos são executar no mesmo núcleo, counter será de 20.

Mas e se esses tópicos são executados em diferentes núcleos ou processadores, poderia haver uma condição de corrida em seu microcódigo? É uma instrução assembler pode sempre ser visto como operação atômica?

Foi útil?

Solução

Especificamente para x86, e sobre o seu exemplo: counter++, há uma série de maneiras que poderia ser compilados. O exemplo mais trivial é:

inc counter

Isto traduz-se o seguinte micro-operações:

  • load counter a um registo escondido na CPU
  • incrementar o registo
  • armazenar o registro atualizado na counter

Este é essencialmente o mesmo que:

mov eax, counter
inc eax
mov counter, eax

Note que, se algumas outras atualizações de agente counter entre a carga ea loja, ele não será refletida na counter após a loja. Este agente pode ser outro segmento no mesmo núcleo, outro núcleo na mesma CPU, outra CPU no mesmo sistema, ou mesmo algum agente externo que usa DMA (Direct Memory Access).

Se você quiser garantir que este inc é atômica, use o prefixo lock:

lock inc counter

garantias lock que ninguém pode atualizar counter entre a carga ea loja.


No que diz respeito instruções mais complicado, você geralmente não pode assumir que eles vão executar atomicamente, a menos que eles apoiam o prefixo lock.

Outras dicas

A resposta é: depende

Aqui estão algumas torno de confusão, o que uma instrução assembler é. Normalmente, uma instrução assembler é traduzido em exatamente uma instrução de máquina. O excemption é quando você usa macros -. Mas você deve estar ciente de que

Dito isto, a questão resume-se é uma instrução de máquina atômica?

Nos bons velhos tempos, era. Mas hoje, com CPUs complexas, longas instruções de funcionamento, hyperthreading, ... não é. Alguns CPUs garantir que alguns aumentar / diminuir instruções são atômicas. A razão é, que eles são puro para muito simples syncronizing.

Além disso, alguns comandos de CPU não são tão problemáticos. Quando você tem um simples fetch (de um pedaço de dados que o processador pode buscar em uma única peça) - a busca em si é claro atômica, porque não há nada para ser dividido em tudo. Mas quando você tem dados não alinhados, torna-se complicado novamente.

A resposta é: depende. Leia atentamente o manual de instruções da máquina do fornecedor. Em caso de dúvida, não é!

Edit: Oh, eu vi isso agora, você também pedir ++ balcão. A afirmação "mais provável de ser traduzida como" não se pode confiar em tudo. Isso depende muito também sobre o compilador, é claro! Fica mais difícil quando o compilador está fazendo otimizações diferentes.

Nem sempre -. Em algumas arquiteturas uma instrução assembly é traduzida em instrução código uma máquina, enquanto em outros não

Além - você pode não supor que a linguagem de programação que você está usando está compilando uma linha aparentemente simples de código em uma instrução de montagem. Além disso, em algumas arquiteturas, você não pode assumir um código de máquina irá executar atomicamente.

Utilize técnicas de sincronização adequada em vez, dependendo da língua que está a codificação em.

  1. Incremento / operações decréscimo em 32-bit ou menos variáveis ??inteiras em um processador de 32-bit único sem Hyper-Threading Technology são atômicas.
  2. Em um processador com tecnologia Hyper-Threading ou em um sistema multi-processador, as operações de aumentar / diminuir não são garantidos para ser executado atomicaly.

Invalidated pelo comentário de Nathan: Se eu me lembro da minha assembler Intel x86 corretamente, a instrução INC só funciona para registos e não diretamente trabalho para posições de memória.

Assim, um contador ++ não seria uma única instrução em assembler (apenas ignorar a parte de pós-incremento). Seria pelo menos três instruções: carga contra variável para registar, registo incremento, registo de volta carga para contador. E isso é apenas para a arquitetura x86.

Em suma, não contar com ele sendo atômica a menos que seja especificado pela especificação da linguagem e que o compilador que você está usando oferece suporte as especificações.

Não, você não pode assumir essa. A menos que claramente indicado na especificação do compilador. E além disso, ninguém pode garantir que uma instrução assembler único fato atômica. Na prática, cada instrução assembler é traduzido para o número de operação microcódigo - UOPs.
também a questão da condição de corrida está intimamente ligado com o modelo de memória (coerência, seqüencial, coerência liberação e etc.), para cada um a resposta e resultado poderia ser diferente.

Outra questão é que se você não declarar a variável como volátil, o código gerado provavelmente não atualizar a memória em cada iteração do loop, apenas no final do ciclo da memória seria atualizado.

Pode não ser uma resposta real à sua pergunta, mas (assumindo que este é C #, ou outra linguagem .NET) se quiser counter++ para realmente ser multi-threaded atômica, você poderia usar System.Threading.Interlocked.Increment(counter).

Veja outras respostas para informações reais sobre as muitas maneiras diferentes porque / como counter++ não poderia ser atômica. ; -)

Na maioria dos casos, não . De fato, em x86, você pode executar a instrução

push [address]

que, em C, seria algo como:

*stack-- = *address;

Este executa duas transferências de memória em uma instrução .

Isso é basicamente impossível fazer em um ciclo de clock, não menos importante, não porque um transferência de memória também é possível em um ciclo!

Em muitos outros processadores, a separação entre o sistema de memória e processador é maior. (Muitas vezes estes processador pode ser pouco ou big-endian dependendo do sistema de memória, como ARM e PowerPC), este também tem consequências para o comportamento atômico se a reordenação do sistema de memória pode lê e escreve.

Para isso, existem barreiras de memória ( http://en.wikipedia.org/wiki/ Memory_barrier )

Assim, em breve, enquanto instruções atômicas são suficientes em intel (com os prefixos de bloqueio relevantes), mais deve ser feito sobre a não-intel, já que a memória de E / S não pode ser na mesma ordem.

Este é um problema conhecido ao portar soluções "livre-lock" da Intel para outras arquiteturas.

(Note que multiprocessador (não multicore) sistemas em x86 também parecem barreiras de memória necessidade, pelo menos no modo de 64 bits.

Eu acho que você vai ter uma condição de corrida no acesso.

Se você queria para garantir uma operação atômica em incrementar contador, então você precisa usar ++ balcão.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top