Pergunta

Eu sei que o modelo de memória .NET (no .NET Framework;not compact/micro/silverlight/mono/xna/what-have-you) garantiu que para certos tipos (principalmente números inteiros primitivos e referências) as operações eram garantidamente atômicas.

Além disso, acredito que a instrução test-and-set x86/x64 (e Interlocked.CompareExchange) na verdade faz referência ao local da memória global, portanto, se for bem-sucedido em outro Interlocked.CompareExchange veria o novo valor.

Por fim, acredito que o volatile palavra-chave é uma instrução para o compilador propagar leituras e gravações o mais rápido possível e não reordenar operações relativas a esta variável (certo?).

Isso leva a algumas perguntas:

  1. Minhas crenças acima estão corretas?
  2. Interlocked.Read não tem sobrecarga para int, apenas para longs (que são 2 WORDs e portanto normalmente não são lidos atomicamente).Sempre presumi que o modelo de memória .NET garantia que o valor mais novo seria visto ao ler ints/referências, porém com caches de processador, registradores, etc.Estou começando a ver que isso pode não ser possível.Então, existe uma maneira de forçar a busca da variável novamente?
  3. Volátil é suficiente para resolver o problema acima para números inteiros e referências?
  4. Em x86/x64 posso assumir que ...

Se houver duas variáveis ​​​​inteiras globais x e y, ambas inicializadas em 0, se eu escrever:

x = 1;
y = 2;

Esse thread NO verá x = 0 e y = 2 (ou seja,as gravações ocorrerão em ordem).Isso muda se eles forem voláteis?

Foi útil?

Solução

  • Somente leituras e gravações em variáveis ​​com no máximo 32 bits de largura (e 64 bits em sistemas x64) são atômicas.Tudo isso significa que você não lerá um interno e obtenha um valor escrito pela metade.Isso não significa que a aritmética seja atômica.
  • As operações interligadas também atuam como barreiras de memória, então sim, Interlocked.CompareExchange verá o valor atualizado.
  • Ver esta página.Volátil não significa ordenado.Alguns compiladores podem optar por não reordenar operações em variáveis ​​voláteis, mas a CPU está livre para reordenar.Se você quiser impedir que a CPU reordene as instruções, use uma barreira de memória (completa).
  • O modelo de memória garante que as leituras e gravações sejam atômicas, e o uso da palavra-chave volátil garante que as leituras serão sempre vêm da memória, não de um registro.Então você vai veja o valor mais novo.Isso ocorre porque as CPUs x86 invalidarão o cache quando apropriado - consulte esse e esse.Veja também IntertravadoCompareExchange64 para saber como ler atomicamente valores de 64 bits.
  • E finalmente, a última pergunta.A resposta é que um tópico poderia de fato ver x = 0 e y = 2, e usar a palavra-chave volátil não muda isso porque a CPU está livre para reordenar as instruções.Você precisa de uma barreira de memória.

Resumo:

  1. O compilador é livre para reordenar instruções.
  2. A CPU está livre para reordenar instruções.
  3. As leituras e gravações do tamanho de palavras são atômicas.As operações aritméticas e outras não são atômicas porque envolvem leitura, cálculo e, em seguida, gravação.
  4. As leituras do tamanho de palavras da memória sempre recuperarão o valor mais recente.Mas na maioria das vezes você não sabe se está realmente lendo de memória.
  5. Uma barreira de memória cheia para (1) e (2).A maioria dos compiladores permite que você pare (1) sozinho.
  6. A palavra-chave volátil garante que você esteja lendo da memória - (4).
  7. As operações interligadas (o prefixo de bloqueio) permitem que múltiplas operações sejam atômicas.Por exemplo, uma leitura + gravação (InterlockedExchange).Ou leitura + comparação + gravação (InterlockedCompareExchange).Eles também atuam como barreiras de memória, portanto (1) e (2) são interrompidos.Eles sempre escrevem na memória (obviamente), então (4) é garantido.

Outras dicas

Me deparei com esse tópico antigo.As respostas de Hans e wj32 estão todas corretas, exceto a parte relativa volatile.

Especificamente em relação à sua pergunta

Em x86/x64 posso assumir que ...Se houver duas variáveis ​​inteiras globais x e y, ambas inicializadas para 0 que, se eu escrever: x = 1; y = 2;

Que nenhum tópico verá x = 0 e y = 2 (ou seja,as gravações ocorrerão em ordem).Isso muda se forem voláteis?

Se y é volátil, a gravação para x é garantido que aconteça antes da gravação para y, portanto, nenhum thread jamais verá x = 0 e y = 2.Isso ocorre porque a gravação em uma variável volátil possui a "semântica de liberação" (logicamente equivalente à emissão de uma barreira de liberação), ou seja,todas as instruções de leitura/gravação antes que ele não passe.(Isso implica que se x for volátil, mas y não, você ainda poderá ver o inesperado x = 0 e y = 2.) Veja a descrição e o exemplo de código no Especificação C# para mais detalhes.

Não, a palavra-chave volátil e a garantia de atomicidade são muito fracas.Você precisa de uma barreira de memória para garantir isso.Você pode obter um explicitamente com o método Thread.MemoryBarrier().

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