Das tue ich nicht Ich denke, die Release- und Acquire-Semantik wird in diesem Fall hilfreich sein. Ein std::memory_order_release auf y.store() würde verwendet werden, um frühere Schreibvorgänge für andere Threads sichtbar zu machen, die den Wert von y gesehen haben, aber in diesem Fall kann Thread1 nichts tun, um die Sichtbarkeit von x auf anderen Threads zu beeinflussen, da x nicht von Thread1 geschrieben wurde. Die Beispiele, die ich in der C++-Dokumentation für Acquire- und Release-Semantik gesehen habe, beziehen sich nur auf ein Paar Threads, bei denen einer reiner Schreiber und der andere reiner Leser ist.
Beispiel:
Code: Select all
std::atomic x;
std::atomic y;
void thread1() {
int val = x.load(std::memory_order_relaxed);
if( val == 42 ){
y.store(1, std::memory_order_relaxed);
}
}
Nachdem ich diese Beispiele erstellt und noch einmal über das Problem nachgedacht habe, kann ich die Frage weiter klären:
- Wenn Sie eine entspannte atomare Ladung haben, gefolgt von einer entspannten atomaren Speicherung auf eine andere Variable, und die Speicherung (durch eine if-Anweisung) vom Ergebnis der Ladung abhängig ist (das ist es, was Thread1 tut). Beispiel), richtet die if-Anweisung dann tatsächlich einen Lade-Speicher-Zaun ein, was bedeutet, dass der Ladevorgang abgeschlossen sein muss, bevor der Speicher für einen anderen Thread (oder eine andere beobachtende Entität wie ein Gerät mit direktem Speicherzugriff) sichtbar werden kann? Stellt die if-Anweisung in der C++-Terminologie ein Vorhergehen zwischen dem Speicher und dem Ladevorgang her? Ärgerlicherweise wird in der C++-Dokumentation zur Speicherreihenfolge nichts über bedingte Speicherungen erwähnt. Im C++-Formalismus wird ein Passes-Before eingerichtet, wenn der Store die Release-Reihenfolge verwendet. Aber die Release-Reihenfolge stoppt auch die Neuordnung von Store-Store und ist daher strenger. Wenn meine Idee also funktioniert und Sie nur die Load-Store-Reihenfolge benötigen, könnte die if-Anweisung ohne explizite Speichergrenzen effizienter sein als eine store_release. Ich halte es für wahrscheinlich, dass dies immer auf echter Hardware funktioniert, aber die C++-Dokumentation ist einfach unvollständig und gibt schwächere Garantien als nötig, was ein Verhalten ermöglicht, das auf echter Hardware in der realen Welt niemals passieren kann.
Code: Select all
// This example is about ordering.
// Will thread3 see the new value of x, if thread1 has seen it?
std::atomic x;
std::atomic y;
void thread1() {
int val = x.load(std::memory_order_relaxed);
if( val == 42 ){
y.store(1, std::memory_order_relaxed);
}
}
void thread2() {
x.store(42, std::memory_order_relaxed);
}
void thread3() {
if( y.load(std::memory_order_relaxed) == 1 ){
std::atomic_thread_fence(memory_order_acquire);
int r = x.load(std::memory_order_relaxed);
assert(r == 42); // Can this go wrong?
}
}
Code: Select all
// Similar, but we're putting it inside a loop
// to make it a little more interesting.
// This focuses on speculative execution.
std::atomic x;
std::atomic y;
std::atomic terminate;
void thread1() {
while( !terminate.load(std::memory_order_relaxed) ){
int val = x.load(std::memory_order_relaxed);
if( val == 42 ){
// Can y.store() be done speculatively?
y.store(1, std::memory_order_relaxed);
break;
}
}
}
void thread2() {
// This will count down and write to x,
// but should never actually store the value 42.
for(int idx = 10000000; idx > 42; --idx){
x.store(idx, std::memory_order_relaxed);
}
sleep_seconds(10);
terminate = true;
}
void thread3() {
while( !terminate.load(std::memory_order_relaxed) ){
if( y.load(std::memory_order_relaxed) == 1 ){
// This should never happen!
Open_Pandoras_Box();
Launch_Nuclear_Missiles();
Unleash_Armageddon();
break;
}
}
}
Mobile version