Ich verstehe, dass dies volatile
den Compiler darüber informiert, dass der Wert möglicherweise geändert wird. Muss der Compiler jedoch einen Speicherzaun einführen, damit diese Funktion funktioniert, um diese Funktionalität zu erreichen?
Nach meinem Verständnis kann die Reihenfolge der Operationen an flüchtigen Objekten nicht neu angeordnet werden und muss beibehalten werden. Dies scheint zu implizieren, dass einige Gedächtniszäune notwendig sind und dass es keinen Weg gibt, dies zu umgehen. Bin ich richtig, wenn ich das sage?
Zu dieser verwandten Frage gibt es eine interessante Diskussion
... Zugriffe auf bestimmte flüchtige Variablen können vom Compiler nicht neu angeordnet werden, solange sie in separaten vollständigen Ausdrücken vorkommen ... Recht, dass flüchtig für die Thread-Sicherheit nutzlos ist, aber nicht aus den von ihm angegebenen Gründen. Dies liegt nicht daran, dass der Compiler die Zugriffe auf flüchtige Objekte möglicherweise neu anordnet, sondern daran, dass die CPU sie möglicherweise neu anordnet. Atomare Operationen und Speicherbarrieren verhindern, dass der Compiler und die CPU neu angeordnet werden
Welchem David Schwartz antwortet in den Kommentaren :
... Aus Sicht des C ++ - Standards gibt es keinen Unterschied zwischen dem Compiler, der etwas tut, und dem Compiler, der Anweisungen ausgibt, die die Hardware veranlassen, etwas zu tun. Wenn die CPU Zugriffe auf flüchtige Stoffe neu anordnen kann, verlangt der Standard nicht, dass ihre Reihenfolge beibehalten wird. ...
... Der C ++ - Standard macht keinen Unterschied darüber, was die Neuordnung bewirkt. Und Sie können nicht behaupten, dass die CPU sie ohne beobachtbaren Effekt neu anordnen kann, also ist das in Ordnung - der C ++ - Standard definiert ihre Reihenfolge als beobachtbar. Ein Compiler ist mit dem C ++ - Standard auf einer Plattform kompatibel, wenn er Code generiert, der die Plattform dazu bringt, das zu tun, was der Standard erfordert. Wenn der Standard verlangt, dass Zugriffe auf flüchtige Stoffe nicht neu angeordnet werden, ist eine Plattform, die sie neu anordnet, nicht konform. ...
Mein Punkt ist, dass, wenn der C ++ - Standard dem Compiler verbietet, Zugriffe auf bestimmte flüchtige Stoffe neu zu ordnen, nach der Theorie, dass die Reihenfolge solcher Zugriffe Teil des beobachtbaren Verhaltens des Programms ist, der Compiler auch Code ausgeben muss, der die CPU daran hindert so. Der Standard unterscheidet nicht zwischen dem, was der Compiler tut, und dem Generierungscode des Compilers, den die CPU ausführt.
Was zwei Fragen aufwirft: Ist eine von ihnen "richtig"? Was machen tatsächliche Implementierungen wirklich?
quelle
volatile
Variablen durch die CPU-Caches optimiert wird. Entweder sind alle diese Compiler nicht konform oder der Standard bedeutet nicht, was Sie denken, dass er bedeutet. (Der Standard unterscheidet nicht zwischen dem, was der Compiler tut, und dem, was der Compiler die CPU macht. Es ist die Aufgabe des Compilers, Code auszugeben, der beim Ausführen dem Standard entspricht.)Antworten:
Lassen
volatile
Sie mich erklären, wann Sie verwenden sollten, anstatt zu erklären , was funktioniertvolatile
.volatile
Variable so ziemlich das einzige ist, was Sie mit dem Standard innerhalb eines Signalhandlers tun können. Seit C ++ 11 können Siestd::atomic
für diesen Zweck verwenden, aber nur, wenn das Atomic gesperrt ist.setjmp
laut Intel .Beispielsweise:
Ohne den
volatile
Bezeichner kann der Compiler die Schleife vollständig optimieren. Dervolatile
Bezeichner teilt dem Compiler mit, dass er möglicherweise nicht davon ausgeht, dass zwei nachfolgende Lesevorgänge denselben Wert zurückgeben.Beachten Sie, dass dies
volatile
nichts mit Threads zu tun hat. Das obige Beispiel funktioniert nicht, wenn ein anderer Thread geschrieben wurde als,*foo
da keine Erfassungsoperation beteiligt ist.In allen anderen Fällen sollte die Verwendung von
volatile
als nicht portierbar betrachtet werden und die Codeüberprüfung nicht mehr bestehen, außer wenn es sich um Compiler und Compiler-Erweiterungen vor C ++ 11 handelt (z. B. den/volatile:ms
Switch von msvc , der standardmäßig unter X86 / I64 aktiviert ist).quelle
setjmp
sind die beiden Garantien, die der Standard macht. Andererseits bestand die Absicht zumindest zu Beginn darin, speicherabgebildete E / A zu unterstützen. Für einige Prozessoren ist möglicherweise ein Zaun oder eine Membar erforderlich.volatile
Zugriffen zu umgehen .Ein C ++ - Compiler, der der Spezifikation entspricht, ist nicht erforderlich, um einen Speicherzaun einzuführen. Ihr spezieller Compiler könnte; Richten Sie Ihre Frage an die Autoren Ihres Compilers.
Die Funktion "flüchtig" in C ++ hat nichts mit Threading zu tun. Denken Sie daran, dass der Zweck von "flüchtig" darin besteht, Compiler-Optimierungen zu deaktivieren, damit das Lesen aus einem Register, das sich aufgrund exogener Bedingungen ändert, nicht optimiert wird. Ist eine Speicheradresse, in die ein anderer Thread auf einer anderen CPU schreibt, ein Register, das sich aufgrund exogener Bedingungen ändert? Nein. Wenn sich einige Compilerautoren dafür entschieden haben , Speicheradressen, auf die von verschiedenen Threads auf verschiedenen CPUs geschrieben wird, so zu behandeln, als würden sie sich aufgrund exogener Bedingungen ändern, ist dies ihre Sache. Sie sind dazu nicht verpflichtet. Sie müssen auch nicht - selbst wenn ein Speicherzaun eingeführt wird - beispielsweise sicherstellen, dass jeder Thread eine konsistente Funktion aufweist Reihenfolge der flüchtigen Lese- und Schreibvorgänge.
Tatsächlich ist flüchtig für das Threading in C / C ++ so gut wie nutzlos. Best Practice ist es, dies zu vermeiden.
Darüber hinaus: Speicherzäune sind ein Implementierungsdetail bestimmter Prozessorarchitekturen. In C #, wo ausdrücklich flüchtig ist für Multithreading ausgelegt, wird die Spezifikation nicht , dass die Hälfte Zäunen sagen wird eingeführt, weil das Programm könnte auf einer Architektur ausgeführt werden , die Zäune in erster Linie nicht verfügt . Vielmehr gibt die Spezifikation wieder bestimmte (extrem schwache) Garantien darüber, welche Optimierungen vom Compiler, der Laufzeit und der CPU vermieden werden, um bestimmte (extrem schwache) Einschränkungen für die Reihenfolge einiger Nebenwirkungen festzulegen. In der Praxis werden diese Optimierungen durch die Verwendung von Halbzäunen beseitigt. Dies ist jedoch ein Implementierungsdetail, das sich in Zukunft ändern kann.
Die Tatsache, dass Sie sich für die Semantik von flüchtig in jeder Sprache interessieren, da sie sich auf Multithreading bezieht, zeigt an, dass Sie darüber nachdenken, Speicher über Threads hinweg zu teilen. Überlegen Sie einfach, das nicht zu tun. Es macht Ihr Programm viel schwieriger zu verstehen und es ist viel wahrscheinlicher, dass es subtile, nicht reproduzierbare Fehler enthält.
quelle
volatile
E / A wurde jedoch in den C-Standard eingeführt. Da der Standard jedoch nicht angeben kann, was bei einem "Zugriff" tatsächlich passiert, heißt es: "Was einen Zugriff auf ein Objekt mit einem flüchtig qualifizierten Typ ausmacht, ist implementierungsdefiniert." Viel zu viele Implementierungen bieten heutzutage keine nützliche Definition eines Zugriffs, was meiner Meinung nach gegen den Geist des Standards verstößt, selbst wenn er dem Buchstaben entspricht.volatile
Die Semantik ist stärker als diese. Der Compiler muss jeden angeforderten Zugriff generieren (1.9 / 8, 1.9 / 12) und nicht nur garantieren, dass exogene Änderungen schließlich erkannt werden (1.10 / 27). In der Welt der speicherabgebildeten E / A kann ein gelesener Speicher eine beliebige zugeordnete Logik haben, wie ein Eigenschafts-Getter. Sie würden Anrufe an Property Getter nicht gemäß den von Ihnen angegebenen Regeln optimierenvolatile
, und der Standard erlaubt dies auch nicht.Was David übersieht, ist die Tatsache, dass der c ++ - Standard das Verhalten mehrerer Threads spezifiziert, die nur in bestimmten Situationen interagieren, und alles andere zu undefiniertem Verhalten führt. Eine Racebedingung mit mindestens einem Schreibvorgang ist undefiniert, wenn Sie keine atomaren Variablen verwenden.
Folglich hat der Compiler das Recht, auf Synchronisierungsanweisungen zu verzichten, da Ihre CPU nur den Unterschied in einem Programm bemerkt, das aufgrund fehlender Synchronisierung ein undefiniertes Verhalten aufweist.
quelle
volatile
hat nichts mit Threads zu tun; Der ursprüngliche Zweck bestand darin, speicherabgebildete E / A zu unterstützen. Zumindest auf einigen Prozessoren würde die Unterstützung von speicherabgebildeten E / A-Vorgängen Zäune erfordern. (Compiler tun dies nicht, aber das ist ein anderes Problem.)volatile
hat viel mit Threads zu tun:volatile
befasst sich mit Speicher, auf den zugegriffen werden kann, ohne dass der Compiler weiß, dass auf ihn zugegriffen werden kann, und der viele reale Verwendungen von gemeinsam genutzten Daten zwischen Threads auf einer bestimmten CPU abdeckt.Erstens garantieren die C ++ - Standards nicht die Speicherbarrieren, die zum ordnungsgemäßen Ordnen der nicht atomaren Lese- / Schreibvorgänge erforderlich sind. Für die Verwendung mit MMIO, die Signalverarbeitung usw. werden flüchtige Variablen empfohlen. Bei den meisten Implementierungen ist flüchtig nicht für Multithreading geeignet und wird im Allgemeinen nicht empfohlen.
In Bezug auf die Implementierung flüchtiger Zugriffe ist dies die Wahl des Compilers.
Dieser Artikel , der das Verhalten von gcc beschreibt, zeigt, dass Sie ein flüchtiges Objekt nicht als Speicherbarriere verwenden können, um eine Folge von Schreibvorgängen in den flüchtigen Speicher zu ordnen.
In Bezug auf das ICC- Verhalten fand ich in dieser Quelle auch, dass Volatile keine Garantie für die Bestellung von Speicherzugriffen garantiert.
Der Microsoft VS2013- Compiler hat ein anderes Verhalten. In dieser Dokumentation wird erläutert, wie flüchtig die Release / Acquire-Semantik erzwingt und die Verwendung flüchtiger Objekte in Sperren / Releases in Multithread-Anwendungen ermöglicht.
Ein weiterer Aspekt, der berücksichtigt werden muss, ist, dass derselbe Compiler möglicherweise ein anderes Verhalten aufweist. je nach angestrebter Hardwarearchitektur zu volatil . In diesem Beitrag zum MSVS 2013-Compiler werden die Besonderheiten des Kompilierens mit volatile für ARM-Plattformen klar angegeben.
Also meine Antwort auf:
wäre: Nicht garantiert, wahrscheinlich nicht, aber einige Compiler könnten es tun. Sie sollten sich nicht darauf verlassen, dass dies der Fall ist.
quelle
volatile
der den Compiler daran hindert, Lasten / Speicher neu zu ordnen? Oder sagen Sie, dass der C ++ - Standard dies erfordert? Und wenn letzteres der Fall ist, können Sie auf mein in der ursprünglichen Frage zitiertes gegenteiliges Argument antworten?volatile
Wert. Da die Definition von "Zugriff" der Implementierung überlassen bleibt, bringt uns dies nicht viel, wenn es der Implementierung egal ist.volatile
, aber es gibt keinen Zaun im generierten Code des Compilers in Visual Studios 2012.volatile
ist, das vom Standard speziell aufgezählt wird. (setjmp
, Signale und so weiter.)Soweit ich weiß, fügt der Compiler nur einen Speicherzaun in die Itanium-Architektur ein.
Das
volatile
Schlüsselwort wird am besten für asynchrone Änderungen verwendet, z. B. für Signalhandler und speicherabgebildete Register. Es ist normalerweise das falsche Werkzeug für die Multithread-Programmierung.quelle
volatile
äußerst nützlich für viele Anwendungen, die sich nie mit Hardware befassen. Verwenden Sie immer, wenn die Implementierung CPU-Code generieren soll, der dem C / C ++ - Code genau folgtvolatile
.Es kommt darauf an, welcher Compiler "der Compiler" ist. Visual C ++ seit 2005. Der Standard verlangt dies jedoch nicht, so dass einige andere Compiler dies nicht tun.
quelle
int volatile i; int main() { return i; }
Erzeugt eine Hauptleitung mit genau zwei Anweisungen :mov eax, i; ret 0;
.cl /help
sagt Version 18.00.21005.1. Das Verzeichnis, in dem es sich befindet, istC:\Program Files (x86)\Microsoft Visual Studio 12.0\VC
. Der Header im Befehlsfenster sagt VS 2013. Also in Bezug auf die Version ... Die einzigen Optionen, die ich verwendet habe, waren/c /O2 /Fa
. (Ohne das/O2
wird auch der lokale Stapelrahmen eingerichtet. Es gibt jedoch noch keine Zaunanweisung.)main
, damit der Compiler das gesamte Programm sehen und wissen konnte, dass es vor mir keine anderen Threads oder zumindest keine anderen Zugriffe auf die Variable gab (es konnte also keine Cache-Probleme geben), könnte dies möglicherweise beeinflussen auch, aber irgendwie bezweifle ich es.Dies ist größtenteils aus dem Speicher und basiert auf Pre-C ++ 11 ohne Threads. Nachdem ich jedoch an Diskussionen über das Threading im Ausschuss teilgenommen habe, kann ich sagen, dass das Komitee nie die Absicht hatte
volatile
, für die Synchronisierung zwischen Threads verwendet zu werden. Microsoft schlug es vor, aber der Vorschlag trug nicht.Die Schlüsselspezifikation von
volatile
ist, dass der Zugriff auf ein flüchtiges Element genau wie IO ein "beobachtbares Verhalten" darstellt. Auf die gleiche Weise kann der Compiler bestimmte E / A nicht neu anordnen oder entfernen, er kann keine Zugriffe auf ein flüchtiges Objekt neu anordnen oder entfernen (oder genauer gesagt, Zugriffe über einen lvalue-Ausdruck mit flüchtigem qualifiziertem Typ). Die ursprüngliche Absicht von volatile bestand darin, speicherabgebildete E / A zu unterstützen. Das "Problem" dabei ist jedoch, dass durch die Implementierung definiert wird, was einen "flüchtigen Zugriff" ausmacht. Und viele Compiler implementieren es so, als wäre die Definition "eine Anweisung, die liest oder in den Speicher schreibt, ausgeführt worden". Dies ist eine legale, wenn auch nutzlose Definition, wenn die Implementierung dies spezifiziert. (Ich habe noch nicht die tatsächliche Spezifikation für einen Compiler gefunden.Wohl (und es ist ein Argument, das ich akzeptiere) verstößt dies gegen die Absicht des Standards, da Sie nicht einmal flüchtig für speicherabgebildete E / A verwenden können, es sei denn, die Hardware erkennt die Adressen als speicherabgebildete E / A und verhindert jegliche Neuordnung usw. Zumindest auf Sparc- oder Intel-Architekturen. Trotzdem gibt keiner der Comiler, die ich mir angesehen habe (Sun CC, g ++ und MSC), Zaun- oder Membar-Anweisungen aus. (Ungefähr zu der Zeit, als Microsoft vorschlug, die Regeln für zu erweitern
volatile
, haben einige ihrer Compiler ihren Vorschlag umgesetzt und Zaunanweisungen für flüchtige Zugriffe ausgegeben. Ich habe nicht überprüft, was die jüngsten Compiler tun, aber es würde mich nicht überraschen, wenn dies davon abhängt Die von mir überprüfte Version - ich glaube, es war VS6.0 - hat jedoch keine Zäune emittiert.)quelle
volatile
Semantik vollkommen ausreichend. Im Allgemeinen melden solche Peripheriegeräte ihre Speicherbereiche als nicht zwischenspeicherbar, was bei der Neuordnung auf Hardwareebene hilfreich ist.ASI_REAL_IO
Teil des Adressraums weisen , sollten Sie in Ordnung sein. (Altera NIOS verwendet eine ähnliche Technik, wobei hohe Bits der Adresse den MMU-Bypass steuern; ich bin sicher, dass es auch andere gibt)Es muss nicht. Flüchtig ist kein Synchronisationsprimitiv. Es deaktiviert nur Optimierungen, dh Sie erhalten eine vorhersehbare Folge von Lese- und Schreibvorgängen innerhalb eines Threads in derselben Reihenfolge, wie sie von der abstrakten Maschine vorgeschrieben wird. Aber Lese- und Schreibvorgänge in verschiedenen Threads haben in erster Linie keine Reihenfolge. Es macht keinen Sinn, davon zu sprechen, ihre Reihenfolge beizubehalten oder nicht beizubehalten. Die Reihenfolge zwischen den Köpfen kann durch Synchronisationsprimitive festgelegt werden. Sie erhalten UB ohne diese.
Ein bisschen Erklärung zu Speicherbarrieren. Eine typische CPU verfügt über mehrere Speicherzugriffsebenen. Es gibt eine Speicherpipeline, mehrere Cache-Ebenen, dann RAM usw.
Membar-Anweisungen spülen die Pipeline. Sie ändern nicht die Reihenfolge, in der Lese- und Schreibvorgänge ausgeführt werden, sondern erzwingen lediglich die Ausführung ausstehender Lesevorgänge zu einem bestimmten Zeitpunkt. Es ist nützlich für Multithread-Programme, aber sonst nicht viel.
Cache (s) sind normalerweise automatisch zwischen den CPUs kohärent. Wenn Sie sicherstellen möchten, dass der Cache mit dem RAM synchronisiert ist, ist eine Cache-Leerung erforderlich. Es unterscheidet sich sehr von einer Membar.
quelle
volatile
nur Compiler-Optimierungen deaktiviert? Das macht keinen Sinn. Jede Optimierung, die der Compiler vornehmen kann, kann zumindest prinzipiell genauso gut von der CPU durchgeführt werden. Wenn der Standard also sagt, dass er nur Compiler-Optimierungen deaktiviert, würde dies bedeuten, dass er kein Verhalten liefert, auf das man sich in portablem Code überhaupt verlassen kann. Dies ist jedoch offensichtlich nicht der Fall, da sich tragbarer Code auf sein Verhalten in Bezug aufsetjmp
und Signale verlassen kann.Der Compiler muss
volatile
nur dann einen Speicherzaun um Zugriffe einführen , wenn dies erforderlich ist, um dievolatile
im Standard angegebenen Verwendungszwecke (setjmp
Signalhandler usw.) auf dieser bestimmten Plattform zu verwenden.Beachten Sie, dass einige Compiler weit über die Anforderungen des C ++ - Standards hinausgehen, um
volatile
auf diesen Plattformen leistungsfähiger oder nützlicher zu werden. Portabler Code sollte nicht darauf angewiesen seinvolatile
, etwas anderes zu tun, als im C ++ - Standard angegeben.quelle
Ich verwende in Interrupt-Serviceroutinen immer flüchtig, z. B. ändert der ISR (häufig Assembly-Code) einen Speicherort, und der Code höherer Ebene, der außerhalb des Interrupt-Kontexts ausgeführt wird, greift über einen Zeiger auf flüchtig auf den Speicherort zu.
Ich mache dies sowohl für RAM als auch für speicherabgebildete E / A.
Basierend auf der Diskussion hier scheint es, dass dies immer noch eine gültige Verwendung von flüchtig ist, aber nichts mit mehreren Threads oder CPUs zu tun hat. Wenn der Compiler für einen Mikrocontroller "weiß", dass es keine anderen Zugriffe geben kann (z. B. ist alles auf dem Chip, kein Cache und es gibt nur einen Kern), würde ich denken, dass ein Speicherzaun überhaupt nicht impliziert ist, der Compiler muss nur bestimmte Optimierungen verhindern.
Während wir mehr Material in das "System" stapeln, das den Objektcode ausführt, sind fast alle Wetten deaktiviert, zumindest habe ich diese Diskussion so gelesen. Wie könnte ein Compiler jemals alle Basen abdecken?
quelle
Ich denke, die Verwirrung um flüchtige und Befehlsumordnungen ergibt sich aus den beiden Begriffen der Neuordnung von CPUs:
Flüchtig beeinflusst, wie ein Compiler den Code unter der Annahme einer Single-Threaded-Ausführung generiert (dies schließt Interrupts ein). Es impliziert nichts über Speicherbarriere-Anweisungen, sondern hindert einen Compiler vielmehr daran, bestimmte Arten von Optimierungen im Zusammenhang mit Speicherzugriffen durchzuführen.
Ein typisches Beispiel ist das erneute Abrufen eines Werts aus dem Speicher, anstatt einen in einem Register zwischengespeicherten Wert zu verwenden.
Ausführung außerhalb der Reihenfolge
CPUs können Anweisungen außerhalb der Reihenfolge / spekulativ ausführen, vorausgesetzt, das Endergebnis könnte im ursprünglichen Code aufgetreten sein. CPUs können Transformationen ausführen, die in Compilern nicht zulässig sind, da Compiler nur Transformationen ausführen können, die unter allen Umständen korrekt sind. Im Gegensatz dazu können CPUs die Gültigkeit dieser Optimierungen überprüfen und sie zurücknehmen, wenn sie sich als falsch herausstellen.
Reihenfolge der Lese- / Schreibvorgänge im Speicher, wie sie von anderen CPUs gesehen werden
Das Endergebnis einer Befehlsfolge, die effektive Reihenfolge, muss mit der Semantik des von einem Compiler generierten Codes übereinstimmen. Die von der CPU gewählte tatsächliche Ausführungsreihenfolge kann jedoch unterschiedlich sein. Die effektive Reihenfolge, wie sie in anderen CPUs zu sehen ist (jede CPU kann eine andere Ansicht haben), kann durch Speicherbarrieren eingeschränkt werden.
Ich bin mir nicht sicher, inwieweit sich die effektive und tatsächliche Reihenfolge unterscheiden kann, da ich nicht weiß, inwieweit Speicherbarrieren CPUs daran hindern können, eine Ausführung außerhalb der Reihenfolge durchzuführen.
Quellen:
quelle
Während ich ein online herunterladbares Video-Tutorial für die Entwicklung von 3D Graphics & Game Engine mit modernem OpenGL durchgearbeitet habe. Wir haben
volatile
in einer unserer Klassen verwendet. Die Tutorial-Website finden Sie hier und das Video, das mit demvolatile
Schlüsselwort arbeitet, befindet sich in derShader Engine
Serie Video 98. Diese Arbeiten sind nicht meine eigenen, sondern akkreditiertMarek A. Krzeminski, MASc
und dies ist ein Auszug aus der Video-Download-Seite.Und wenn Sie seine Website abonniert haben und Zugriff auf seine Videos in diesem Video haben, verweist er auf diesen Artikel über die Verwendung
Volatile
mit dermultithreading
Programmierung.Hier ist der Artikel über den obigen Link: http://www.drdobbs.com/cpp/volatile-the-multithreaded-programmers-b/184403766
Dieser Artikel ist vielleicht etwas veraltet, gibt aber einen guten Einblick in eine hervorragende Verwendung des flüchtigen Modifikators bei der Verwendung von Multithread-Programmierung, um Ereignisse asynchron zu halten, während der Compiler die Rennbedingungen für uns überprüft. Dies beantwortet möglicherweise nicht direkt die ursprüngliche Frage des OP zum Erstellen eines Speicherzauns, aber ich entscheide mich, dies als Antwort für andere zu veröffentlichen, um eine hervorragende Referenz für eine gute Verwendung von Volatile bei der Arbeit mit Multithread-Anwendungen zu finden.
quelle
Das Schlüsselwort
volatile
bedeutet im Wesentlichen, dass das Lesen und Schreiben eines Objekts genau so ausgeführt werden sollte , wie es vom Programm geschrieben wurde, und in keiner Weise optimiert werden sollte . Binärcode sollte C- oder C ++ - Code folgen: eine Last, in der dieser gelesen wird, ein Speicher, in dem geschrieben wird.Dies bedeutet auch, dass von keinem Lesevorgang ein vorhersehbarer Wert erwartet werden sollte: Der Compiler sollte auch unmittelbar nach einem Schreibvorgang in dasselbe flüchtige Objekt nichts über einen Lesevorgang annehmen:
volatile
ist möglicherweise das wichtigste Werkzeug in der Toolbox "C ist eine Assemblersprache auf hoher Ebene" .Ob das Deklarieren eines Objekts als flüchtig ausreicht, um das Verhalten von Code sicherzustellen, der sich mit asynchronen Änderungen befasst, hängt von der Plattform ab: Unterschiedliche CPUs bieten unterschiedliche Ebenen der garantierten Synchronisation für normale Lese- und Schreibvorgänge im Speicher. Sie sollten wahrscheinlich nicht versuchen, einen solchen Multithreading-Code auf niedriger Ebene zu schreiben, es sei denn, Sie sind ein Experte auf diesem Gebiet.
Atomprimitive bieten eine schöne übergeordnete Ansicht von Objekten für Multithreading, die es einfach macht, über Code nachzudenken. Fast alle Programmierer sollten entweder atomare Grundelemente oder Grundelemente verwenden, die gegenseitige Ausschlüsse wie Mutexe, Lese- / Schreibsperren, Semaphoren oder andere blockierende Grundelemente bieten.
quelle