C ++ std :: memory_order_relaxed confusão
Eu estava lendo este artigo do GCC Wiki sobre as barreiras de memória C ++ (e isso é incrível).
Foi muito simples até chegar a este ponto:
A abordagem oposta é std :: memory_order_relaxed. Este modelo permite muito menos sincronização, removendo as restrições de acontecer antes. Esses tipos de operações atômicas também podem ter várias otimizações realizadas neles, como remoção de armazenamento morto e commoning. Portanto, no exemplo anterior:
-Thread 1-
y.store (20, memory_order_relaxed)
x.store (10, memory_order_relaxed)
-Thread 2-
if (x.load (memory_order_relaxed) == 10)
{
assert (y.load(memory_order_relaxed) == 20) /* assert A */
y.store (10, memory_order_relaxed)
}
-Thread 3-
if (y.load (memory_order_relaxed) == 10)
assert (x.load(memory_order_relaxed) == 10) /* assert B */
Uma vez que os threads não precisam ser sincronizados no sistema, qualquer declaração neste exemplo pode realmente FALHAR.
Ok, isso também é simples, vamos continuar ..
-Thread 1-
x.store (1, memory_order_relaxed)
x.store (2, memory_order_relaxed)
-Thread 2-
y = x.load (memory_order_relaxed)
z = x.load (memory_order_relaxed)
assert (y <= z)
A afirmação não pode falhar. Uma vez que o armazenamento de 2 é visto pelo thread 2, ele não pode mais ver o valor 1. Isso evita a coalescência de cargas relaxadas de uma variável em cargas relaxadas de uma referência diferente que pode ser alias.
Isso é o que me confundiu, por que y não pode carregar o valor 2 ez carregar o valor 1 (e fazer com que a asserção falhe), já que a ordenação não está sincronizada na thread 1?
Respostas
A ordenação relaxada é relativa à ordenação de operações com relação a outros acessos à memória , não a ordenação relativa ao atômico sendo modificado por relaxamento. Em seu primeiro caso, o fato de você poder ver 10 em xnão significa nada em relação ao valor de y. E vice versa.
Mas seu segundo caso é diferente, porque está afetando o mesmo objeto atômico.
[intro.races] / 10 nos diz que, dentro de uma thread, se uma operação é sequenciada antes da outra, então essa operação "acontece antes" da outra. E [intro.races] / 14-17 delineia o seguinte comportamento em relação a átomos :
Os quatro requisitos de coerência anteriores efetivamente desautorizam a reordenação do compilador de operações atômicas para um único objeto, mesmo se ambas as operações forem cargas relaxadas.
E é isso que você tem aqui. Todas as modificações estão acontecendo no mesmo objeto, então elas devem acontecer em alguma ordem. Mesmo que essa ordem não possa ser determinada com exatidão, ela deve respeitar os relacionamentos "acontece antes" do código.
As duas operações do thread 1 são ordenadas por um relacionamento "acontece antes". E as operações do Thread 2 são ordenadas por um relacionamento "acontece antes".
Como todos estão agindo no mesmo objeto atômico, se yobtiver o valor 2, então deve ter "acontecido depois" de ter xsido definido como 2. Portanto, a ordem de acesso para xdeve ter sido "x = 1, x = 2, leia x ". E como a última leitura de xacontece após a primeira leitura de x, o valor obtido não pode ser 1.