mia comprensione di std::memory_order_acquire
e std::memory_order_release
è la seguente:recinzioni di memoria: acquisire/carico e rilasciare/negozio
Acquisisci significa che nessun accessi alla memoria che appaiono dopo la recinzione acquisiscono possono essere riordinati a prima della recinzione .
uscita significa che nessun accessi alla memoria che appaiono prima la recinzione di rilascio possono essere riordinati per dopo la recinzione.
Quello che non capisco è il motivo per cui con la libreria atomica C++ 11 in particolare, la fence di acquisizione è associata alle operazioni di caricamento, mentre la fence fence è associata alle operazioni di archiviazione.
Per chiarire, la libreria C++ 11 <atomic>
consente di specificare le recinzioni di memoria in due modi: o è possibile specificare un recinto come un argomento in più per un'operazione atomica, come:
x.load(std::memory_order_acquire);
Oppure si può usare std::memory_order_relaxed
e specificare il recinto a parte, come:
x.load(std::memory_order_relaxed);
std::atomic_thread_fence(std::memory_order_acquire);
Quello che non capisco è, date le definizioni di cui sopra di acquisire e rilasciare, perché C++ 11 associare specificamente acquisiscono con carico e versione con negozio? Sì, ho visto molti degli esempi che mostrano come è possibile utilizzare un acquisisci/carica con un release/store per sincronizzare i thread, ma in generale sembra che l'idea di acquisire le fence (evitare il riordino della memoria dopo l'istruzione) e il rilascio recinti (previene il riordino della memoria prima dell'affermazione) è ortogonale all'idea di carichi e depositi.
Quindi, perché, per esempio, non il compilatore lasciatemi dire:
x.store(10, std::memory_order_acquire);
mi rendo conto che posso realizzare quanto sopra utilizzando memory_order_relaxed
, e poi una atomic_thread_fence(memory_order_acquire)
dichiarazione separata, ma ancora una volta, perché ci riesco Posso usare lo store direttamente con memory_order_acquire
?
Un caso d'uso possibile per questo potrebbe essere se voglio assicurare che qualche negozio, dicono x = 10
, accade prima qualche altra dichiarazione esegue che potrebbe influenzare altri thread.
In un tipico algoritmo lock-free, si legge un atomico per verificare se una risorsa condivisa è pronta per il consumo (pronta per essere acquisita) e si scrive un atomico per indicare che una risorsa condivisa è pronta per essere utilizzata (per rilasciare la risorsa). Non vuoi che le letture della risorsa condivisa si spostino prima che la guardia atomica sia controllata; e non vuoi che l'inizializzazione della risorsa da condividere sia spostata dopo che l'atomico è stato scritto, indicando il rilascio. –
Nell'esempio solo 'atomic_thread_fence (std :: memory_order_acquire)' è una vera barriera. Vedi ** 1.10: 5 Esecuzioni multi-thread e dati razze [intro.multithread] ** nello standard, che dice (citando la bozza n3797) _ "Un'operazione di sincronizzazione senza una posizione di memoria associata è una fence e può essere o una acquisire recinzione, una recinzione di rilascio, o entrambe una recinzione di acquisizione e di rilascio. "_ Al contrario,' x.load (std :: memory_order_acquire) 'è una operazione _atomica_ che esegue un'operazione _acquire_ su' x', sarebbe una _sincronizzazione operazione_ se il valore corrisponde a un negozio _release_ in x. – amdn
Nell'introduzione lo standard (bozza n3797) non limita le operazioni di acquisizione ai carichi e rilascia le operazioni ai negozi. Questo è sfortunato. Devi andare alla clausola ** 29.3: 1 Ordine e consistenza [atomics.order] ** per trovare _ "memory_order_acquire, memory_order_acq_rel e memory_order_seq_cst: un'operazione di caricamento esegue un'operazione di acquisizione sulla posizione di memoria interessata" _ e _ "memory_order_release , memory_order_acq_rel e memory_order_seq_cst: un'operazione di archiviazione esegue un'operazione di rilascio nella posizione di memoria interessata "_ – amdn