Kann jemand erklären, was std::memory_order
im Klartext ist und wie man sie benutzt std::atomic<>
?
Ich habe die Referenz und einige Beispiele hier gefunden, verstehe sie aber überhaupt nicht. http://en.cppreference.com/w/cpp/atomic/memory_order
Kann jemand erklären, was std::memory_order
im Klartext ist und wie man sie benutzt std::atomic<>
?
Ich habe die Referenz und einige Beispiele hier gefunden, verstehe sie aber überhaupt nicht. http://en.cppreference.com/w/cpp/atomic/memory_order
Antworten:
Kann jemand erklären, was std :: memory_order im Klartext ist,
Die beste Erklärung für "Plain English", die ich für die verschiedenen Speicherreihenfolgen gefunden habe, ist Bartoz Milewskis Artikel über entspannte Atomik: http://bartoszmilewski.com/2008/12/01/c-atomics-and-memory-ordering/
Und der Follow-up-Beitrag: http://bartoszmilewski.com/2008/12/23/the-inscrutable-c-memory-model/
Beachten Sie jedoch, dass diese Artikel zwar eine gute Einführung sind, jedoch vor dem C ++ 11-Standard liegen und nicht alles enthalten, was Sie wissen müssen, um sie sicher zu verwenden.
und wie man sie mit std :: atomic <> benutzt?
Mein bester Rat an Sie hier ist: Nicht . Entspannte Atomics sind (wahrscheinlich) die schwierigste und gefährlichste Sache in C ++ 11. Halten Sie sich an std::atomic<T>
die Standard-Speicherreihenfolge (sequentielle Konsistenz), bis Sie wirklich sicher sind, dass Sie ein Leistungsproblem haben, das mithilfe der entspannten Speicherreihenfolge gelöst werden kann.
Im zweiten oben verlinkten Artikel kommt Bartoz Milewski zu folgendem Schluss:
Ich hatte keine Ahnung, worauf ich mich einließ, als ich versuchte, über schwache C ++ - Atomics nachzudenken. Die Theorie dahinter ist so komplex, dass sie unbrauchbar ist. Es waren drei Personen (Anthony, Hans und ich) und eine Änderung des Standards erforderlich, um den Beweis eines relativ einfachen Algorithmus zu vervollständigen. Stellen Sie sich vor, Sie tun dasselbe für eine Warteschlange ohne Sperren, die auf schwachen Atomen basiert!
relaxed
ist viel einfacher zu verstehen: Verwenden Sie es, wenn Sie nur Atomizität und keine Synchronisation benötigen. Beispiel: Für einen einzelnen Atomzähler werden mehrere Threads erhöht, aber das hat keine Bedeutung dafür, ob andere Daten gültig sind.
Mit den std::memory_order
Werten können Sie feinkörnige Einschränkungen für die Speicherreihenfolge festlegen, die durch Ihre atomaren Operationen bereitgestellt wird. Wenn Sie ändern und Atom-Variablen von mehreren Threads zugegriffen wird , dann die vorbeifahrenden std::memory_order
Werte für Ihren Betrieb können Sie entspannen die Einschränkungen für den Compiler und Prozessor über die Reihenfolge , in der die Operationen auf diesen atomaren Variablen zu anderen Threads sichtbar werden, und die Synchronisation Auswirkungen dieser Operationen auf die nichtatomaren Daten in Ihrer Anwendung.
Die Standardreihenfolge von std::memory_order_seq_cst
ist die am stärksten eingeschränkte und bietet die "intuitiven" Eigenschaften, die Sie möglicherweise erwarten: Wenn Thread A einige Daten speichert und dann ein atomares Flag mit setzt std::memory_order_seq_cst
, kann Thread B sehen, dass das Flag gesetzt ist, wenn er sieht, dass das Flag gesetzt ist Die anderen Werte für die Speicherreihenfolge bieten nicht unbedingt diese Garantie und müssen daher sehr sorgfältig verwendet werden.
Die Grundvoraussetzung lautet: Verwenden Sie nichts anderes als std::memory_order_seq_cst
(die Standardeinstellung), es sei denn (a) Sie wissen wirklich wirklich , was Sie tun, und können beweisen, dass die entspannte Verwendung in allen Fällen sicher ist, und (b) Ihr Profiler zeigt, dass die Datenstruktur und Operationen, mit denen Sie die entspannten Bestellungen verwenden möchten, sind ein Engpass.
Mein Buch C ++ Concurrency in Action widmet ein ganzes Kapitel (45 Seiten) den Details des C ++ - Speichermodells, den atomaren Operationen und den std::memory_order
Einschränkungen sowie ein weiteres Kapitel (44 Seiten) der Verwendung atomarer Operationen zur Synchronisation in sperrenfreien Datenstrukturen und die Konsequenzen lockerer Ordnungsbeschränkungen.
Meine Blogeinträge zum Dekker-Algorithmus und zum Peterson-Algorithmus zum gegenseitigen Ausschluss zeigen einige der Probleme.
bool shouldHalt
weil ich einen Thread gebeten habe, aufzuhören. Nach meiner Erfahrung ist eine solche Isolation in der normalen Softwareentwicklung nicht selten. Und ich behaupte, dass für den anderen Fall (Abhängigkeiten zwischen mehreren Variablen) eine Sperre fast immer besser ist. Der einzige Weg, wie ich sehe, dass Menschen Schwierigkeiten damit haben, sind ausgefallene atomar verknüpfte Datenstrukturen, auf die man wirklich achten sollte, es sei denn, es besteht ein ernsthafter Bedarf.
std::memory_order
in Abschnitt 5.2.1 viele verschiedene Ordnungen (noch bevor Sie erklären, was sie in Abschnitt 5.3.3 tatsächlich tun), anstatt sich an std::memory_order_seq_cst
die in dieser Antwort vorgeschlagene Reihenfolge zu halten . Bedeutet dies, dass diese Antwort (oder das Buch) falsch ist, oder habe ich etwas falsch verstanden?
std::memory_order_seq_cst
sei denn, Sie wissen wirklich, was Sie tun, und es handelt sich um einen nachgewiesenen Leistungsengpass.
Nein . A „einfache Englisch“ Erklärung nimmt 32 Seiten und kann gefunden werden hier .
Wenn Sie das nicht lesen möchten, können Sie die Speicherreihenfolge vergessen, da auf der von Ihnen verlinkten Seite angegeben wird, dass die Standardeinstellung eine sequentiell konsistente Reihenfolge ist, die die Einstellung "Immer das Vernünftige tun" lautet.
Um eine andere Einstellung zu verwenden, müssen Sie das obige Papier und die darin enthaltenen Beispiele wirklich lesen und verstehen.
Kurz gesagt, Ihr Compiler und Ihre CPU können Anweisungen in einer anderen Reihenfolge ausführen, als Sie sie geschrieben haben. Für einen einzelnen Thread ist dies kein Problem, da es korrekt erscheint. Bei mehreren Threads auf mehreren Prozessoren wird dies zu einem Problem. Die Speicherreihenfolge in C ++ schränkt die Funktionen Ihres Compilers / Ihrer CPU ein und behebt solche Probleme.
Wenn Sie sich zum Beispiel meinen Artikel über das Überprüfen von Sperren ansehen, können Sie sehen, wie das Bestellen von Fehlern mit diesem Muster - es wird erwähnt, dass die Reihenfolge des atomaren Speichers verwendet werden kann, um das Problem zu beheben.
Über die Neuordnung selbst können Sie auch eine CPU-Neuordnung in Betracht ziehen - auch hier führt der Compiler möglicherweise auch Neuordnungen durch .
Beachten Sie, dass alle Dokumente zu diesem Thema (einschließlich meiner) von theoretischen Szenarien sprechen. Die gängigsten CPUs wie x86 haben sehr starke Bestellgarantien, so dass viele explizite Ordnungen einfach nicht erforderlich sind. Selbst wenn Sie nicht die richtige C ++ 11-Atomik verwenden, funktioniert Ihr Code wahrscheinlich immer noch.
Wie zvrba erwähnte, ist das Thema eigentlich ziemlich detailliert. Das Linux-Kernel-Dokument zu Speicherbarrieren enthält auch viele detaillierte Informationen.
Es gibt etwas einfaches Englisch im GCC-Wiki. ;)