Looking for c++ Keywords? Try Ask4Keywords

C++C ++ 11-Speichermodell


Bemerkungen

Verschiedene Threads, die versuchen, auf denselben Speicherplatz zuzugreifen, nehmen an einem Datenwettlauf teil, wenn mindestens eine der Operationen eine Modifikation ist (auch als Speicheroperation bezeichnet ). Diese Datenrennen verursachen undefiniertes Verhalten . Um dies zu vermeiden, muss verhindert werden, dass diese Threads gleichzeitig in Konflikt stehende Vorgänge ausführen.

Synchronisationsprimitive (Mutex, kritischer Abschnitt und dergleichen) können solche Zugriffe schützen. Das in C ++ 11 eingeführte Speichermodell definiert zwei neue tragbare Methoden zum Synchronisieren des Zugriffs auf den Speicher in einer Multithread-Umgebung: atomare Operationen und Zäune.

Atomoperationen

Es ist jetzt möglich, den angegebenen Speicherplatz mithilfe von Atomlast- und Atomspeicheroperationen zu lesen und zu schreiben. Der Einfachheit halber werden diese in die Vorlagenklasse std::atomic<t> . Diese Klasse wickelt einen Wert vom Typ t , aber dieses Mal lädt und speichert zum Objekt ist atomar.

Die Vorlage ist nicht für alle Typen verfügbar. Welche Typen verfügbar sind, ist implementierungsspezifisch, dies schließt jedoch in der Regel die meisten (oder alle) verfügbaren Integraltypen sowie Zeigertypen ein. Damit sollten std::atomic<unsigned> und std::atomic<std::vector<foo> *> verfügbar sein, während std::atomic<std::pair<bool,char>> höchstwahrscheinlich nicht der Fall sein wird.

Atomare Operationen haben folgende Eigenschaften:

  • Alle atomaren Operationen können gleichzeitig von mehreren Threads ausgeführt werden, ohne dass es zu einem undefinierten Verhalten kommt.
  • Bei einer atomaren Last wird entweder der Anfangswert angezeigt, mit dem das atomare Objekt erstellt wurde, oder der Wert, der über eine atomare Speicheroperation in dieses Objekt geschrieben wird.
  • Atomspeicher für dasselbe Atomobjekt sind in allen Threads gleich angeordnet. Wenn ein Thread bereits den Wert einer atomaren Speicheroperation gesehen hat, wird bei nachfolgenden atomaren Ladeoperationen entweder derselbe Wert angezeigt oder der Wert, der von der nachfolgenden atomaren Speicheroperation gespeichert wird.
  • Atomare Lese-, Änderungs- und Schreiboperationen ermöglichen die atomare Last und den atomaren Speicher , ohne dass ein anderer atomarer Speicher dazwischen liegt. Beispielsweise kann man einen Zähler aus mehreren Threads atomar inkrementieren, und unabhängig von der Konkurrenz zwischen den Threads geht kein Inkrement verloren.
  • Atomare Operationen erhalten einen optionalen Parameter std::memory_order , der definiert, welche zusätzlichen Eigenschaften die Operation in Bezug auf andere Speicherorte hat.
std :: memory_order Bedeutung
std::memory_order_relaxed keine zusätzlichen einschränkungen
std::memory_order_releasestd::memory_order_acquire Wenn für load-acquire Ladebereitschaft der von store-release gespeicherte Wert angezeigt wird store-release Filialen sequenziert, bevor die store-release , bevor die Ladevorgänge nach der Ladebereitschaft erfolgen
std::memory_order_consume Wie memory_order_acquire aber nur für abhängige Ladungen
std::memory_order_acq_rel kombiniert load-acquire und store-release
std::memory_order_seq_cst sequentielle Konsistenz

Diese Speicherordnungs-Tags ermöglichen drei verschiedene Speicherordnungsdisziplinen: sequenzielle Konsistenz , entspannt und Release-Acquisition mit dem Release-Consum von Geschwistern.

Sequenzielle Konsistenz

Wenn für eine atomare Operation keine Speicherreihenfolge angegeben wird, wird die Reihenfolge standardmäßig auf sequentielle Konsistenz festgelegt . Dieser Modus kann auch explizit ausgewählt werden, indem die Operation mit std::memory_order_seq_cst .

Bei dieser Reihenfolge kann keine Speicheroperation die atomare Operation kreuzen. Alle Speicheroperationen, die vor der atomaren Operation sequenziert wurden, finden vor der atomaren Operation statt, und die atomare Operation findet statt vor allen Speicheroperationen, die nach dieser Sequenz ausgeführt werden. Dieser Modus ist wahrscheinlich der einfachste Grund, über den er nachdenken kann, aber er führt auch zu der größten Leistungseinschränkung. Außerdem werden alle Compiler-Optimierungen verhindert, die andernfalls versuchen würden, Operationen nach der atomaren Operation neu zu ordnen.

Entspannte Bestellung

Das Gegenteil von sequentieller Konsistenz ist die entspannte Speicheranordnung. Es wird mit dem Tag std::memory_order_relaxed . Durch den entspannten atomaren Betrieb werden keine anderen Speicheroperationen eingeschränkt. Der einzige Effekt, der bleibt, ist, dass die Operation selbst noch atomar ist.

Bestellung freigeben

Eine Atomspeicheroperation kann mit Tags versehen wird std::memory_order_release und ein Atomlastbetrieb kann mit Tags versehen wird std::memory_order_acquire . Die erste Operation wird als (atomare) Speicherfreigabe bezeichnet, während die zweite als (atomare) Lastakquisition bezeichnet wird .

Bei Last-acquire den Wert von einem Speicher-Release geschrieben sieht , geschieht folgendes: alle Speicheroperationen vor dem Laden-Release sequenziert werden sichtbar (geschehen vor) Ladevorgänge , die nach dem Last-acquire sequenziert werden.

Atomische Lese-, Änderungs- und Schreiboperationen können auch das kumulative Tag std::memory_order_acq_rel . Dies macht die Atomlast Teil des Betriebs eine Atom Last-acquire , während der Atomspeicherteil Atom-Store-Release wird.

Der Compiler darf keine Speicheroperationen nach einer atomaren Speicherfreigabe verschieben . Es ist auch nicht zulässig, Ladeoperationen vor der atomaren Lastaufnahme (oder Lastaufnahme ) zu verschieben.

Beachten Sie auch, dass es keine atomare Lastfreigabe oder atomare Speichererfassung gibt . Wenn Sie versuchen, solche Operationen zu erstellen, werden die Operationen entspannt .

Bestellung freigeben

Diese Kombination ähnelt Release- std::memory_order_consume , aber dieses Mal wird die atomare Last mit std::memory_order_consume und wird zu einer (atomaren) load- std::memory_order_consume - Operation. Dieser Modus ist derselbe wie bei der Freigabeerfassung, mit dem einzigen Unterschied, dass unter den nach dem Lastverbrauch aufeinanderfolgenden Ladeoperationen nur diese in Abhängigkeit von dem durch den Lastverbrauch geladenen Wert angeordnet werden.

Zäune

Zäune ermöglichen auch die Anordnung von Speicheroperationen zwischen Threads. Ein Zaun ist entweder ein Freigabezaun oder ein Zaun.

Wenn ein Freigabezaun vor einem Erfassungszaun auftritt, sind die vor dem Freigabezaun sequenzierten Speicher für Ladungen sichtbar, die nach dem Erfassungszaun sequenziert wurden. Um zu gewährleisten, dass der Freigabezaun vor dem Erfassungszaun geschieht, können andere Synchronisationsprimitive verwendet werden, einschließlich entspannter atomarer Operationen.

C ++ 11-Speichermodell Verwandte Beispiele