volatile notwendig?


  • Mod

    Stefan schrieb:

    Angenommen wir haben diesen Pseudocode:

    ...
    

    Wäre x volatile, dann müsste 200 herauskommen, so kommen aber möglicherweise nur 100 raus. Ist jemand einverstanden? 😃

    Abgelehnt. volatile sorgt nur dafür, dass irgendetwas zwischen 100 und 200 herauskommen muss.



  • Stefan schrieb:

    Angenommen wir haben diesen Pseudocode:

    Wäre x volatile, dann müsste 200 herauskommen, so kommen aber möglicherweise nur 100 raus. Ist jemand einverstanden? 😃

    Nein. Auch wenn x volatile ist kann da 100 herauskommen. Mit oder ohne ist es falsch.





  • Ponto schrieb:

    Stefan schrieb:

    Angenommen wir haben diesen Pseudocode:

    Wäre x volatile, dann müsste 200 herauskommen, so kommen aber möglicherweise nur 100 raus. Ist jemand einverstanden? 😃

    Nein. Auch wenn x volatile ist kann da 100 herauskommen. Mit oder ohne ist es falsch.

    Könntest du das begründen? Ist ++x nicht atomar?



  • camper schrieb:

    1. Betrachte 2 Threads, einer schreibt permanent abwechselnd einen von zwei Werten an eine Speicherzelle; der andere liest permanent den Wert (wir nehmen an, dass beide Zugriffe "atomar" erfolgen, es wird also immer nur ein Wert gelesen, der so auch mal geschrieben wurde). Keine besonderen Synchronisationsmechanismen sollen aktiv sein, nur soll der Zugriff jeweils per volatile Variable erfolgen. Können wir irgendeine gesicherte Aussage über die Abfolge der gelesenen Werte machen?
    2. Gleiches System, der schreibende Thread schreibt erst einen Wert (ständig). Von einem zufälligen Zeitpunkt an, schreibt er ständig einen zweiten Wert. Wie sieht es jetzt aus?

    Naja das ist mir schon klar denke ich.

    Zum 1. - der zweite Thread wird irgendeine Folge der beiden Werte lesen. Manchmal den selben doppelt, manchmal "übersieht" er welche, manchmal stimmt die "Reihenfolge" zufällig. Das hängt allein vom Scheduler ab. Die gesicherte Aussage ist, dass wir mindestens einen der beiden Werte sehen werden. Im Worst-Case den anderen garnicht.

    Zum . 2 - irgendwann, zu einem zufälligen Zeitpunkt, der nicht zeitgleich zum Zeitpunkt des Schreibens des anderen Wertes sein muss (und i.d.R. auch nicht ist), wird der zweite Thread den zweiten Wert lesen.

    Meine Aussagen waren im Prinzip auch nicht direkt auf Threads bezogen, sondern auf asynchrone Änderungen, z.B. durch ein signal. Mein Verständnis von volatile war bisher immer, dass es dem Compiler sagt, dass er, obwohl eigentlich kein Aliasing vorliegen kann, trotzdem davon ausgeht, dass Aliasing vorliegt und er keine Optimierungen in diese Richtung macht.



  • Hihi.
    Nein, x++ ist nicht atomar. Der C++ Standard kenn AFAIK (noch) nichtmal den Ausdruck atomar.

    Warum x++ nicht atomar ist? Einfach so, weil keiner sagt dass es atomar sein müsste. Beispiel:

    load x to register
    increment register
    store register to x
    

    Wo ist das jetzt Atomar?
    Davon abgesehen, selbst wenn der erzeugte Assembler-Code einfach nur "increment x" wäre, selbst das wäre nicht notwendigerweise atomar. Ist es im übrigen auch nichtmal auf der "alles verzeihenden" x86 Architektur. Wäre VIEL zu aufwändig das überall zu garantieren, wo es doch in 99.9% der Fälle vollkommen überflüssig ist. Soll heissen selbst wenn nur ein einziger Assembler Befehlt irgendwo steht ist noch lange lange nicht garantiert dass die Durchführung dieses Befehls atomar ist. Im Gegenteil, die x86 Architektur kennt nur ganz wenige Befehle die man atomar machen kann, und nur einer davon ist es "von Haus aus" (ohne das "Lock Prefix").

    Meine Aussagen waren im Prinzip auch nicht direkt auf Threads bezogen, sondern auf asynchrone Änderungen, z.B. durch ein signal. Mein Verständnis von volatile war bisher immer, dass es dem Compiler sagt, dass er, obwohl eigentlich kein Aliasing vorliegen kann, trotzdem davon ausgeht, dass Aliasing vorliegt und er keine Optimierungen in diese Richtung macht.

    volatile hat mit Aliasing nixe zu tun, volatile sagt dem Compiler bloss dass er einen Wert als "beobachtbar" behandeln soll, wobei jetzt "beobachtbar" auch bedeutet dass der Compiler davon ausgehen muss dass der Wert sich ohne das Zutun des Programmes ändert.

    volatile verbietet allerdings viele Dinge eben grade NICHT, z.B. Reordering an vielen Stellen etc. volatile sorgt auch NICHT dafür dass Werte atomar geschrieben oder gelesen werden.

    Und nochmal: wenn man Mutexen korrekt einsetzt braucht man weder volatile noch sonstige Tricks aus der Zauberkiste.



  • Ponto schrieb:

    Volatile hilft bei Threads überhaupt nicht. In diesem Zusammenhang sollte es überhaupt nicht verwendet werden.

    Siehe zum Beispiel die nicht so alte Diskussion:
    http://groups.google.com/group/comp.programming.threads/browse_frm/thread/dee6c3e9af4124e6/79a4a0d192563109?lnk=st&q=volatile+comp.programming.threads#79a4a0d192563109

    Hallo,

    wenn du mit "Zusammenhang" Synchronisierung meinst, dann bin ich einverstanden.
    Sonst nicht.
    Volatile funktioniert mit Threads und kann entsprechend seiner Bedeutung eingesetzt werden. Zum Beispiel um Signale an die Threads zu senden.

    jenz



  • jenz schrieb:

    Volatile funktioniert mit Threads und kann entsprechend seiner Bedeutung eingesetzt werden.

    Volatile hat in Bezug auf Threads keine Bedeutung, weil der Standard nichts über Nebenläufigkeit sagt. Das ist hier nun doch schon mehrfach gesagt worden.

    Nur weil du einen Compiler benutzt, bei dem das trotzdem funktioniert, ist das nicht allgemeingültig.

    jenz schrieb:

    Zum Beispiel um Signale an die Threads zu senden.

    Ja, mach du mal. Aber wundere dich nicht, wenn alles zusammenbricht, wenn du den Compiler wechselst.



  • dann erklär mir das doch bitte mal.

    ein signal könnte zum beispiel sein, dass der thread sich beenden soll.
    wenn ich also eine variable

    volatile bool end;

    habe, dann kann ich die innerhalb des threads abfragen und wenn end true ist, dann beendet sich der thread.

    das ist das signal, das ich von einem anderen thread setzen kann.

    wieso sollte das nicht immer gehen?
    leider habe ich den standard nicht, aber ich finde auch nichts im netz, was dem widerspricht.
    kann jemand den standard zitieren?

    jenz



  • @jenz: Sehe ich auch so. Allerdings sollte man wohl besser sig_atomic_t für das Signal nehmen.



  • Scott Meyers and Andrei Alexandrescu schrieb:

    First, the Standard's constraints on observable behavior are only for an abstract machine defined by the Standard, and that abstract machine has no notion of multiple threads of execution. As a result, though the Standard prevents compilers from reordering reads and writes to volatile data within a thread, it imposes no constraints at all on such reorderings across threads. At least that's how most compiler implementers interpret things. As a result, in practice, many compilers may generate thread-unsafe code from the source above. If your multithreaded code works properly with volatile and doesn't work without, then either your C++ implementation carefully implemented volatile to work with threads (less likely), or you simply got lucky (more likely). Either case, your code is not portable.

    Quelle:
    http://www.aristeia.com/Papers/DDJ_Jul_Aug_2004_revised.pdf



  • hier:

    http://www.ddj.com/cpp/184403766

    ist vom guten Andrei Alexandrescu noch was zu volatile und da wird auch genau das beispiel genommen, das ich vorhin erwähnt hatte.

    Worauf man sich definitiv nicht verlassen kann ist, wenn mehrere _schreibende_ threads damit (möglicherweise ungewollt) synchronisiert werden sollen.
    Das ist auch das Problem bei dem Singeltons in dem PDF.

    Aber wenigstes dieses Signalproblem ist mit volatile lösbar.

    So, jetzt bitte noch andere.

    jenz



  • jenz schrieb:

    http://www.ddj.com/cpp/184403766

    Hast du das selbst mal ganz gelesen?

    Da steht, dass volatile nicht das tut, was du erwartest, wenn du es einfach auf eingebaute Typen anwendest.

    jenz' Link schrieb:

    The compiler compiles ++ctr_ if ctr_ is volatile, although the generated code is simply incorrect.

    ...

    Don't use volatile directly with primitive types.



  • Hallo MFK,

    wenn du das so aus dem Zusammenhang reißt, dann passt das nicht, das stimmt.
    Ich habe es ganz gelesen, also:

    When writing multithreaded programs, you can use volatile to your advantage. You must stick to the following rules:

    Define all shared objects as volatile.
    Don't use volatile directly with primitive types.
    When defining shared classes, use volatile member functions to express thread safety.

    Man kann es zu seinem Vorteil einsetzen, wie er es beschreibt, wenn man sich an diese Regeln hält. Das verbietet aber nicht, dass man es auch einfach für primitive Typen nutzt.

    und bei dem anderen textausschnitt sieht es ähnlich aus:

    Why? Because with Counter, the compiler will not warn you if you mistakenly access ctr_ directly (without locking it). The compiler compiles ++ctr_ if ctr_ is volatile, although the generated code is simply incorrect.

    Da ging es darum, dass man den Code einmal mit locking und einmal ohne anspricht...

    jenz



  • jenz schrieb:

    Man kann es zu seinem Vorteil einsetzen, wie er es beschreibt, wenn man sich an diese Regeln hält. Das verbietet aber nicht, dass man es auch einfach für primitive Typen nutzt.

    Natürlich verbietet es das nicht. Es bedeutet aber auch, dass man den von Alexandrescu beschriebenen Aufwand betreiben muss, um threadsicheren Code zu erzeugen. Ein einfaches "volatile bool end;" erfüllt diesen Zweck IMHO nicht.

    Volatile bedeutet nicht automatisch threadsicher.



  • MFK schrieb:

    Volatile bedeutet nicht automatisch threadsicher.

    Ich habe gerade nochmal den ganzen Thread gelesen - das hat doch auch niemand behauptet 😕



  • genau, volatile bedeutet nicht threadsicher, das habe ich auch nicht behauptet!

    aber Veränderungen in einer Variable kann man mit volatile beobachten.

    nachtrag:
    wobei man nicht notwendigerweise jede änderung mitbekommen muss.



  • @jenz
    Du vermischt hier immerfort 2 Dinge. Einmal den volatile Trick, wo man ausnutzt dass man auf ein "volatile X&" nur volatile Methoden aufrufen darf (analog zu const), und dann die Sache dass volatile bestimmte Optimierungen unterdrückt.

    Das eine wird vom Standard garantiert, das andere nicht.

    ----

    Was sig_atomic_t angeht: der Standard sagt über sig_atomic_t sehr sehr wenig, und das nur im Zusammenhang mit dem "eingebauten" Signal-Handling. Daraus Rückschlüsse über irgendein Verhalten bezüglich Threads zu ziehen erscheint zwar logisch, ist aber falsch.


  • Mod

    Define all shared objects as volatile.

    Kann man machen, ist aber nicht notwendig und sehr schlecht für die Effizienz. Schließlich will man die Dauer, für die man einen gelockten Mutex beansprucht, möglichst klein halten. Das passiert nicht, da die volatile-Semantik aber auch innerhalb eines kritischen Abschnittes unnötig aktiv ist. Und ohne Synchronisationsmechanismen sollte man shared-Objekte nie anfassen - es verbleibt daher kein Fall, bei dem volatile zweckmäßig ist. Auch mit volatile hat ein shared Objekt ohne expliziten Mechanismus in der Regel keinen stabilen Zustand.

    Don't use volatile directly with primitive types.

    wofür sonst?

    When defining shared classes, use volatile member functions to express thread safety.

    Kann man evtl. machen, funktioniert wahrscheinlich auch. Ist aber - wie schon erwähnt - unterspezifiziert. Das beginnt schon mit der einfachen Frage, ob der Aufruf einer virtuellen Funktion eines volatile-Objektes ein Zugriff im volatile-Sinne ist.




Anmelden zum Antworten