C ++ std :: memory_order_relaxed confusione
Stavo leggendo questo articolo da GCC Wiki sulle barriere di memoria C ++ (ed è fantastico).
È stato abbastanza semplice finché non sono arrivato a questo punto:
L'approccio opposto è std :: memory_order_relaxed. Questo modello consente una sincronizzazione molto inferiore rimuovendo le restrizioni accade prima. Questi tipi di operazioni atomiche possono anche avere varie ottimizzazioni eseguite su di essi, come la rimozione del punto morto e il commoning. Quindi nell'esempio precedente:
-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 */
Poiché i thread non devono essere sincronizzati attraverso il sistema, entrambe le asserzioni in questo esempio possono effettivamente FAIL.
Ok, anche questo è semplice, andiamo avanti ..
-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)
L'asserzione non può fallire. Una volta che l'archivio di 2 è stato visto dal thread 2, non può più vedere il valore 1. Ciò impedisce la fusione dei carichi rilassati di una variabile attraverso carichi rilassati di un riferimento diverso che potrebbero alias.
Questo è ciò che mi ha confuso, perché y non può caricare il valore 2 e z caricare il valore 1 (e causare il fallimento dell'asserzione), poiché l'ordinamento non è sincronizzato nel thread 1?
Risposte
L'ordinamento rilassato è relativo all'ordinamento delle operazioni rispetto ad altri accessi alla memoria , non l'ordinamento relativo all'atomico che viene modificato in modo rilassato. Nel tuo primo caso, il fatto che puoi vedere 10 in xnon significa nulla per quanto riguarda il valore di y. E viceversa.
Ma il tuo secondo caso è diverso, perché riguarda lo stesso oggetto atomico.
[intro.races] / 10 ci dice che, all'interno di un thread, se un'operazione è in sequenza prima di un'altra, allora quell'operazione "avviene prima" dell'altra. E [intro.races] / 14-17 delineano il seguente comportamento riguardo all'atomica :
I quattro requisiti di coerenza precedenti non consentono in modo efficace il riordino da parte del compilatore di operazioni atomiche su un singolo oggetto, anche se entrambe le operazioni sono carichi rilassati.
Ed è quello che hai qui. Tutte le modifiche stanno accadendo allo stesso oggetto, quindi devono avvenire in un certo ordine. Anche se quell'ordine non può essere determinato esattamente, l'ordine deve rispettare le relazioni "accade prima" del codice.
Le due operazioni del thread 1 sono ordinate da una relazione "accade prima". E le operazioni di Thread 2 sono a loro volta ordinate da una relazione "accade prima".
Poiché agiscono tutti sullo stesso oggetto atomico, se yottiene il valore 2, deve essere xstato impostato su 2 "è accaduto dopo" . Quindi l'ordine di accesso per xdeve essere stato "x = 1, x = 2, leggi x ". E poiché l'ultima lettura di xavviene dopo la prima lettura di x, il valore che ottiene non può essere 1.