Warum wird das flüchtige Qualifikationsmerkmal in std :: atomic verwendet?


72

Nach dem, was ich von Herb Sutter und anderen gelesen habe, würde man denken, dass die volatilegleichzeitige Programmierung völlig orthogonale Konzepte waren, zumindest was C / C ++ betrifft.

In der GCC- Implementierung haben jedoch alle std::atomicMitgliedsfunktionen das volatileQualifikationsmerkmal. Gleiches gilt für Anthony Williams ' Implementierung von std::atomic.

Also, was ist los , müssen meine atomic<>Variablen sein volatileoder nicht?


+1 Mr. Williams ist hier auf SO, vielleicht kann er auftauchen und eine Antwort geben :)
AraK

1
Ich habe auf comp.std.c ++ eine Frage dazu gesehen. Denken volatileSie daran, dass Garantien, die in einem einzelnen Thread lesen und schreiben, in der richtigen Reihenfolge ausgeführt werden und dass auf einem flüchtigen Objekt keine nichtflüchtigen Elementfunktionen aufgerufen werden können (genau wie bei const). Aber darüber hinaus habe ich keine Ahnung von Threads in C ++. Jedes Mal, wenn ich versuche, im Standard darüber zu lesen, fange ich an aufzugeben, da ich nicht in der Lage bin, die schiere Menge an Indirektionen und Logiken im Text xD
Johannes Schaub - litb

Antworten:


57

Warum wird das volatileQualifikationsmerkmal durchgehend verwendet std::atomic?

Damit flüchtige Objekte auch atomar sein können. Siehe hier :

Das entsprechende Zitat ist

Die Funktionen und Operationen sind so definiert, dass sie mit flüchtigen Objekten arbeiten, sodass Variablen, die flüchtig sein sollten, auch atomar sein können. Das flüchtige Qualifikationsmerkmal ist jedoch für die Atomizität nicht erforderlich.

Müssen meine atomic<>Variablen sein volatileoder nicht?

Nein, atomare Objekte müssen nicht flüchtig sein.


Hinweis: Dies ist technisch identisch mit plattformspezifischen stackoverflow.com/questions/3708160/…
Suma

Das flüchtige Qualifikationsmerkmal wird verwendet, um eine Neuordnung zu verhindern. Das ist es, was es tut.
Michaël Roy

4
@ MichaëlRoy: volatileOperationen werden nur schriftlich bestellt. andere volatileZugriffe. atomic<T>Freigabe-, Erwerbs- und seq_cst-Operationen werden in der Reihenfolge bestellt. Ebene nicht-atomare Variablen, also Ihre eigene atomics rollt mit nur volatile können es nicht geben Ihnen die gleiche acq / rel Semantik ohne Barrieren. volatile atomic<T>kann / wird in Zukunft nützlich sein, wenn Compiler die Als-ob-Regel nutzen, um die Atomik zu optimieren , z. B. um zu verhindern, dass "redundante" Schreibvorgänge mit einem Fortschrittszähler zusammengeführt werden.
Peter Cordes

1
@ MichaëlRoy: Es ist technisch gesehen immer noch UB mit Ausnahme von volatile sig_atomic_t, aber die meisten realen Implementierungen haben, intdass dies für Ladungen / Speicher natürlich atomar ist (Definition des Verhaltens). (Ich habe kürzlich eine große Antwort dazu geschrieben: MCU-Programmierung - C ++ O2-Optimierung bricht während der Schleife ab ) Trotzdem, wenn Sie eine Bestellung wünschen. Nicht-atomare Variablen (z. B. Interrupt-Handler schreibt einen normalen Puffer und setzt dann ein atomares Flag) können Sie eine entspannte atomare Last und atomic_signal_fence(memory_order_acquire)im Hauptcode verwenden, um eine Neuordnung zur Kompilierungszeit zu verhindern.
Peter Cordes

1
@ MichaëlRoy: Ja, ich weiß genau, was volatilebei echten Compilern passiert. Die Anzahl der Dinge, die Sie sicher für die Interaktion zwischen einem Signal- oder Interrupt-Handler tun können, ist jedoch sehr begrenzt, und es hilft nicht, wenn Typen zu breit sind, um von Natur aus atomar zu sein. Aber solange du dabei bleibst, funktioniert es. Wie ich im Link in meinem vorherigen Kommentar gezeigt habe, können Sie C ++ 11 Relaxed Atomics verwenden signal_fence, um alles, volatilewas für diesen Anwendungsfall möglich ist, genauso effizient und mit der Option zum Freigeben / Erfassen von Bestellungen mithilfe von zu tun signalf_fence.
Peter Cordes

79

Um zusammenzufassen, was andere richtig geschrieben haben:

C / C ++ volatileist für Hardwarezugriff und Interrupts vorgesehen. C ++ 11 atomic<>dient zur Kommunikation zwischen Threads (z. B. in sperrenfreiem Code). Diese beiden Konzepte / Verwendungen sind orthogonal, aber sie haben überlappende Anforderungen, und deshalb haben die Leute die beiden oft verwechselt.

Der Grund atomic<>für flüchtig qualifizierte Funktionen ist der gleiche Grund für const-qualifizierte Funktionen, da es im Prinzip möglich ist, dass ein Objekt sowohl atomic<>als auch constund / oder ist volatile.

Wie mein Artikel hervorhob, ist eine weitere Quelle der Verwirrung natürlich, dass C / C ++ volatilenicht mit C # / Java identisch ist volatile(letzteres entspricht im Wesentlichen C ++ 11 atomic<>).


2
Ich werde die Tatsache missbrauchen, dass Sie hier sind, um Ihre Meinung zu einem Artikel von Alexandrescu über die Verwendung des volatileFlags zu erfragen , um Fehler bei der Kompilierung von Thread-unsicherem Code zu erzeugen (indem volatileInstanzen verwendet werden, um die Verwendung der Schnittstelle zu sperren und const_castdas Flüchtige zu entfernen wenn ein Mutex erworben wird). Könnte es sinnvoll sein, zu diesem Zweck ein Typqualifikationsmerkmal 'threadsafe' oder ähnliches in die Sprache aufzunehmen (ich denke nur laut nach) ?
David Rodríguez - dribeas

10
In einigen Artikeln von Andrei war es das, was er wirklich tat, das flüchtige Schlüsselwort als ein handliches, meist nicht verwendetes Tag in dem Typsystem zu entführen (ähm, ich meine, "wiederverwenden"), das er als Hook verwenden konnte, um zu überladen und andere Effekte zu erzielen , was ein wenig verwirrend war, weil es nicht ganz so gesagt wurde.
Herb Sutter

1
Siehe auch Dr. Alexandrescus (ich bin nicht mit Vornamen) Follow-up-Kommentare und nicht ganz Rückzüge zu diesem flüchtigen Artikel ( sozusagen
Metall

15

Als const ist flüchtig transitiv. Wenn Sie eine Methode als deklarieren volatile, können Sie keine nichtflüchtige Methode oder eines ihrer Mitgliedsattribute aufrufen. Mit std::atomicMethoden volatileerlauben Sie Aufrufe von volatileMitgliedsmethoden in Klassen, die die std::atomicVariablen enthalten .

Ich habe keinen guten Tag ... so verwirrend ... vielleicht hilft ein kleines Beispiel:

struct element {
   void op1() volatile;
   void op2();
};
struct container {
   void foo() volatile {
      e.op1();  // correct
      //e.op2();  // compile time error
   }
   element e;
};
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.