Wie werden atomare und nichtatomare Operationen in C ++ gemischt?

8

Die std :: atomic Typen erlauben atomaren Zugriff auf Variablen, aber ich würde manchmal wie nicht-atomarer Zugriff, zum Beispiel wenn der Zugriff durch einen Mutex geschützt ist. Betrachten Sie eine Bitfield-Klasse, die beide Multithread-Zugriff erlaubt (über Einfügen) und single-threaded vektorisierter Zugriff (über Operator | =):

%Vor%

Note operator | = ist sehr nah an dem, was in meinem echten Code ist, aber insert (std :: set) ist versuche nur die Idee zu erfassen, dass man

%Vor%

Meine Frage ist: Was ist der beste Weg, um atomare und nicht-atomare zu mischen? Zugriff? Antworten auf [1,2] unten deuten darauf hin, dass Casting falsch ist (und ich stimme zu). Aber sicher erlaubt der Standard solchen scheinbar sicheren Zugang?

Allgemeiner, kann man eine Leser-Schreiber-Sperre verwenden und erlauben, dass "Leser" atomar lesen und schreiben, und der einzigartige "Schreiber" nicht-atomisch lesen und schreiben kann?

Referenzen

  1. Wie std :: atomic effizient zu verwenden
  2. Zugriff auf atomare & lt; int & gt; von C ++ 0x als nicht atomar
fritzo 02.09.2012, 16:36
quelle

2 Antworten

5

Standard C ++ vor C ++ 11 hatte kein Multithread-Speichermodell. Ich sehe keine Änderungen in dem Standard, der das Speichermodell für nicht-atomare Zugriffe definieren würde, daher erhalten diese ähnliche Garantien wie in einer Umgebung vor C ++ 11.

Es ist theoretisch sogar noch schlimmer als die Verwendung von memory_order_relaxed , weil das Cross-Thread-Verhalten von nicht-atomaren Zugriffen einfach völlig undefiniert ist, im Gegensatz zu mehreren möglichen Ausführungsreihenfolgen, von denen eine letztendlich passieren muss.

Um solche Muster beim Mischen von atomaren und nicht-atomaren Zugriffen zu implementieren, müssen Sie sich immer noch auf plattformspezifische Nicht-Standard-Konstrukte (z. B. _ReadBarrier ) und / oder intime Kenntnisse bestimmter Hardware verlassen.

Eine bessere Alternative ist es, sich mit memory_order enum vertraut zu machen und zu hoffen, mit einem gegebenen Code und Compiler eine optimale Assembly-Ausgabe zu erreichen. Das Endergebnis kann korrekt und portabel sein und keine unerwünschten Speicherzäune enthalten, aber Sie sollten erwarten, dass Sie zuerst mehrere fehlerhafte Versionen disassemblieren und analysieren, wenn Sie wie ich sind. und es wird immer noch keine Garantie geben, dass die Verwendung von atomaren Zugriffen auf alle Codepfade nicht zu überflüssigen Zäunen in einer anderen Architektur oder einem anderen Compiler führt.

Die beste praktische Antwort ist also zuerst die Einfachheit. Gestalten Sie Ihre Cross-Thread-Interaktionen so einfach wie Sie es schaffen können, ohne Skalierbarkeit, Reaktionsfähigkeit oder irgendeine andere heilige Kuh vollständig zu töten; haben fast keine gemeinsamen veränderbaren Datenstrukturen; und greife so selten wie möglich auf sie zu, immer atomar.

    
Jirka Hanika 02.09.2012, 19:59
quelle
4

Wenn Sie dies tun könnten, hätten Sie (möglicherweise) einen Thread, der ein Datenobjekt mit atomaren Zugriffen liest / schreibt, und einen anderen Thread, der dasselbe Datenobjekt liest / schreibt, ohne atomare Zugriffe zu verwenden. Das ist ein Datenrennen und das Verhalten wäre undefiniert.

    
Pete Becker 02.09.2012 19:26
quelle