Thread safety / Atomarer Zugriff auf primitive Datentypen



  • Hallo,

    Schau mal ob Du das Buch "Win32 Multithreaded Programming" ISBN 1-56592-296-4 noch Irgendwo bekommst. Mir hat dieses Buch sehr geholfen.

    Bei unseren Applikationen laufen meistens 20 bis 100 Threads und fast alle benutzen CriticalSections für die Synchronisation. Ich hatte noch nie ein Geschwindigkeitsproblem mit den CriticalSections.

    Ich steuere die Threads mit Events ich finde das ist die schönere Methode als mit dem boolean.

    Der boolean sollte eigentlich aber auch gehen, mindestens der sollte Atomar sein.

    Ich würde ihn aber als

    volatile bool  running;
    

    Deklarieren, ich glaube er könnte sonst wegoptimiert werden.

    Herzliche Grüsse
    Walter



  • Es gibt keine atomaren Typen, nur atomare Operationen. Aber das ist bei deinen Schleifen bool eh alles irrelevant, den brauchst du nicht zu locken, da reicht volatile aus.
    Ansonsten schau vielleicht auch mal hier: http://msdn.microsoft.com/en-us/library/windows/desktop/ms686360.aspx
    Die WinAPI bietet einen Haufen atomarer Primitive...


  • Mod

    +1 für die Posts von weicher und dot



  • Danke für die Antworten, weicher und dot!

    @weicher: Langsam wird mein Programm bisher noch nicht, ich habs ja noch nicht programmiert 😉 Bevor ich mir die ganze Arbeit mache und hinterher feststelle, es hackt irgendwo, wollte ich mal hier im Forum nachfragen. Gut zu wissen dass CriticalSections keine Probleme machen werden. Ich schaue mal ob es das Buch in der Uni Bibliothek gibt.

    @dot: Ich weiß, es gibt allerhand Funktionen für sowas. Ich kenne die Seite. Wollte nur wissen ob und wann es auch wirklich nötig ist, solche Funktionen zu benutzen. Du sagst jetzt z.B. bei einem bool brauche ich diese Funktionen nicht. Gilt das nur für bool oder auch noch für weitere primitive Datentypen? Ich meine, ich kann schon überall wo ich mir nicht sicher bin, diese Funktionen benutzen. Allerdings wäre es mir lieber, wenn sie nur dort benutze wo es auch wirklich nötig ist und sonst nirgends.



  • Ich glaub du hast eine grundlegend falsche Vorstellung davon, wofür solche atomaren Operationen gut sind. Es ist jedenfalls nicht so, dass man einfach für Alles diese Funktionen benutzt und der Code dann auf magische Weise threadsafe wird. Abgesehen davon möchte ich nochmals betonen dass "atomarer Datentyp" keinen Sinn macht. Es gibt nur atomare Operationen. Die Aussage dass bool atomar wäre macht keinen Sinn. Und Operationen auf einem bool oder int oder sonstwas sind im Allgemeinen ganz gewiss nicht atomar.



  • Ich habe nie etwas von atomaren Datentypen geschrieben. Im Titel steht "Atomarer Zugriff auf primitive Datentypen" - damit sind Lese- und Schreibzugriffe gemeint. Sorry wenn das irreführend ausgedrückt war.

    dot schrieb:

    Und Operationen auf einem bool oder int oder sonstwas sind im Allgemeinen ganz gewiss nicht atomar.

    Dass z.B. die Anweisung

    i++;
    

    aus mehreren Maschinenbefehlen besteht, die mitten in der Ausführung unterbrochen werden können, ist mir klar.
    Aber Keine Ahnung ob eine Zuweisung schon als Operation gilt oder nicht. Kann eine Zuweisung unterbrochen werden? Und was ist mit Zeigern? Wird eine Anweisung wie

    *i = 5;
    

    aufgeteilt in eine Dereferenzierung und eine Zuweisung oder ist das auch nur eine Maschinenanweisung, die nicht unterbrochen werden kann?

    dot schrieb:

    Ich glaub du hast eine grundlegend falsche Vorstellung davon, wofür solche atomaren Operationen gut sind. Es ist jedenfalls nicht so, dass man einfach für Alles diese Funktionen benutzt und der Code dann auf magische Weise threadsafe wird.

    Mag sein, dass ich da was falsch verstanden habe. Ich denke es ist so: Wenn mehrere Threads lesend und schreibend auf ein ganze struct zugreifen bzw. mehrere Variablen lesen/ändern wollen, muss ich natürlich synchronisieren, sonst bekommt ein lesender Thread möglicherwiese inkonsistente Daten wenn er während des Lesens von einem schreibenden Thread unterbrochen wird.

    Ist das soweit richtig?

    Aber ich wollte doch nur, dass mein Programm nicht abstürzt wenn zwei Threads gleichzeitig auf eine Variable zugreifen. Deshalb: Kann ein Thread unterbrochen werden, wenn er einen primitiven Datentyp liest? Wenn ja, dann muss ich das verhindern, wenn nein, dann nicht. Kann sowas überhaupt zu einem Absturz führen oder ist diese Befürchtung völliger Quatsch?



  • SchlechterInformatiker schrieb:

    Ich habe nie etwas von atomaren Datentypen geschrieben. Im Titel steht "Atomarer Zugriff auf primitive Datentypen" - damit sind Lese- und Schreibzugriffe gemeint. Sorry wenn das irreführend ausgedrückt war.

    Ich wollt es nur extra betonen. Die Antwort auf diese Frage lautet immer noch: Das kann man allgemein nicht sagen. Das hängt von allem möglichen ab, evtl. bis hin zu irgendwelchen Compilerflags.

    dot schrieb:

    Kann eine Zuweisung unterbrochen werden?

    Das kann man allgemein nicht sagen. Eine Zuweisung an einen int der richtig aligned ist (!) wird vermutlich meistens atomar sein. Aber bei einem long ist das schon überhaupt nichtmehr so sicher. Der könnte auf der jeweiligen Plattform z.B. zwei Worte haben...

    SchlechterInformatiker schrieb:

    Und was ist mit Zeigern? Wird eine Anweisung wie

    *i = 5;
    

    aufgeteilt in eine Dereferenzierung und eine Zuweisung oder ist das auch nur eine Maschinenanweisung, die nicht unterbrochen werden kann?

    Auch hier wieder: Hängt davon ab. Evtl. muss z.B. zuerst die Adresse gelesen werden...

    Das einzige was man dazu allgemein sagen kann ist: Von solchen Annahmen sollte man sich am besten einfach nicht abhängig machen.

    SchlechterInformatiker schrieb:

    Mag sein, dass ich da was falsch verstanden habe. Ich denke es ist so: Wenn mehrere Threads lesend und schreibend auf ein ganze struct zugreifen bzw. mehrere Variablen lesen/ändern wollen, muss ich natürlich synchronisieren, sonst bekommt ein lesender Thread möglicherwiese inkonsistente Daten wenn er während des Lesens von einem schreibenden Thread unterbrochen wird.

    Ist das soweit richtig?

    Ja.

    SchlechterInformatiker schrieb:

    Aber ich wollte doch nur, dass mein Programm nicht abstürzt wenn zwei Threads gleichzeitig auf eine Variable zugreifen.

    Das kann nicht passieren.

    SchlechterInformatiker schrieb:

    Deshalb: Kann ein Thread unterbrochen werden, wenn er einen primitiven Datentyp liest?

    Hängt davon ab, diese Frage lässt sich allgemein nicht beantworten. Inwiefern ist das denn ein Problem?

    SchlechterInformatiker schrieb:

    Kann sowas überhaupt zu einem Absturz führen oder ist diese Befürchtung völliger Quatsch?

    Wenn dein Programm abstürzt, dann weil es versucht mit kaputten Daten zu arbeiten. Allein nur weil zwei Threads gleichzeitig auf die selbe Speicherstelle schreiben oder von der selben Stelle lesen passiert ganz sicher nichts.



  • Hallo,

    dot meinte mich 😉

    diese Aussage ist ihm sauer aufgestossen

    Der boolean sollte eigentlich aber auch gehen, mindestens der sollte Atomar sein.

    Wenn man diesen Satz für sich alleine Betrachtet, dann hat dot natürlich Recht.

    Aber wir waren ja ganz klar bei

    while (running)
    {
      ...
    }
    

    Natürlich hätte ich besser "mindestens dieser Zugriff sollte Atomar sein" gesagt.

    Herzliche Grüsse
    Walter



  • dot schrieb:

    Die Aussage dass bool atomar wäre macht keinen Sinn. Und Operationen auf einem bool oder int oder sonstwas sind im Allgemeinen ganz gewiss nicht atomar.

    Im Prinzip richtig!

    Laut Intel gibt es "basic memory operations" die atomar sind. Leider können Hochsprachenbefehle aber aus mehreren Maschinenbefehlen bestehen.

    Wenn eine Konstante (z.B. true, false) per atomarer "basic memory operation" gelesen oder geschrieben wird, scheint das aber hier zu gehen.

    Bei INC, DEC und anderen ist jedoch die Verwendung von LOCK dokumentiert, woraus
    man schliesssen kann, das es notwendig ist sich den Datenbus exlusiv zu sichern.
    (Datenwort lesen, inkrementieren und zurückschreiben ist bei mehreren CPUs wohl
    nicht sicher ...)

    SchlechterInformatiker schrieb:

    Dass z.B. die Anweisung

    i++;
    

    aus mehreren Maschinenbefehlen besteht, die mitten in der Ausführung unterbrochen werden können, ist mir klar.

    Genau da liegt das Problem. i++ ist EIN Maschinenbefehl (inc), aber TROTZDEM
    NICHT atomar ...



  • Ok, und was ist denn nun mit Zuweisungen? Sind die atomar?
    Kann höchstens "nur" vorkommen, dass eben ein veralteter Wert gelesen wird weil ein lesender Thread unterbrochen wurde und inzwischen ein schreibender Thread einen neuen Wert geschrieben hat?

    Oder kann da sogar ein Programmabsturz vorkommen wenn ein Thread während des Zugriffs auf eine Variable unterbrochen wird (nicht dass ich das bisher hätte, will aber trotzdem sicher gehen und vorbeugen)?



  • dot schrieb:

    SchlechterInformatiker schrieb:

    Und was ist mit Zeigern? Wird eine Anweisung wie

    *i = 5;
    

    aufgeteilt in eine Dereferenzierung und eine Zuweisung oder ist das auch nur eine Maschinenanweisung, die nicht unterbrochen werden kann?

    Auch hier wieder: Hängt davon ab. Evtl. muss z.B. zuerst die Adresse gelesen werden...

    [/quote]

    Wo wir gerade dabei sind: Im zitierten Programming Guide ist übrigens nur die Rede von Speicherzugriffen. Es gibt Instruktionen, die haben sowohl Lese- als auch Schreibzugriffe. Natürlich wird der Prozessor nicht unterbrochen, wenn er mitten in einer Instruktion steckt. Deswegen heisst es aber noch lange nicht, dass sich in einer Mehrprozessorumgebung zeitlich ein anderer Prozessor zwischen die Speicherzugriffe schieben könnte.



  • merano schrieb:

    dot schrieb:

    Die Aussage dass bool atomar wäre macht keinen Sinn. Und Operationen auf einem bool oder int oder sonstwas sind im Allgemeinen ganz gewiss nicht atomar.

    Im Prinzip richtig!

    Laut Intel gibt es "basic memory operations" die atomar sind. Leider können Hochsprachenbefehle aber aus mehreren Maschinenbefehlen bestehen.

    Natürlich ist auf Maschinenebene ganz klar definiert was atomar ist und was nicht. Aber hier gehts um C++ und da ist das eben nicht definiert und man sollte daher auch keine Annahmen über solche Dinge treffen 😉



  • Aaah Thread-Safety! Ich war gerade dabei meinen letzten Post zu editieren...

    Können Programmabstürze vorkommen?


  • Mod

    [quote="SchlechterInformatiker"]Ok, und was ist denn nun mit Zuweisungen? Sind die atomar?

    Steht doch alles in dem posting von Merano:

    Guaranteed Atomic Operations
    The Intel486 processor (and newer processors since) guarantees that the following basic memory operations will always
    be carried out atomically:
    . Reading or writing a byte
    . Reading or writing a word aligned on a 16-bit boundary
    . Reading or writing a doubleword aligned on a 32-bit boundary
    The Pentium processor (and newer processors since) guarantees that the following
    additional memory operations will always be carried out atomically:
    . Reading or writing a quadword aligned on a 64-bit boundary
    . 16-bit accesses to uncached memory locations that fit within a 32-bit data bus
    The P6 family processors (and newer processors since) guarantee that the following
    additional memory operation will always be carried out atomically:
    . Unaligned 16-, 32-, and 64-bit accesses to cached memory that fit within a cache line

    [quote="SchlechterInformatiker"]
    Oder kann da sogar ein Programmabsturz vorkommen wenn ein Thread während des Zugriffs auf eine Variable unterbrochen wird (nicht dass ich das bisher hätte, will aber trotzdem sicher gehen und vorbeugen)?

    Wie denn das bitte?
    Wir reden hier doch von atomaren Operationen in dem Sinn wie es Mearon schreibt. Wie soll überhaupt so etwas einen Programmabsturz verursachen?



  • x86 schrieb:

    Natürlich wird der Prozessor nicht unterbrochen, wenn er mitten in einer Instruktion steckt. Deswegen heisst es aber noch lange nicht, dass sich in einer Mehrprozessorumgebung zeitlich ein anderer Prozessor zwischen die Speicherzugriffe schieben könnte.

    genau. 100 Punkte. (s.o.)



  • @Martin Richter: Weiß ich nicht... das frage ich mich auch. Bei atomaren Operationen natürlich nicht. Deiner Antwort entnehme ich, dass das nicht vorkommen kann. Aber was gilt als atomare Operation und was nicht?

    Bezüglich des Postings von Merano:
    Da steht "The Intel486 processor"... was ist wenn aber mein Programm auf einem AMD oder sonst was läuft?



  • Hallo dot,

    Aber hier gehts um C++

    Jetzt bin ich spitzfindig 😃

    Hier gehts ganz klar um MFC(Visual C++) und daher um Windows.

    Herzliche Grüsse
    Walter



  • Nur um es nochmal ganz klar und deutlich zu sagen: Es ist nicht definiert ob und unter welchen Umständen irgendwelche Operationen in C++ atomar sind oder nicht und daher kannst du dich nicht drauf verlassen. Period.
    Wenn du atomare Operationen brauchst, dann musst du dich entsprechender Primitive bedienen, wie z.B. die erwähnten Funktionen der WinAPI oder irgendwelche Compiler Intrinsics oder sonstwas. C++11 bietet auch atomics in der Standardlibrary, die sind nur noch nicht überall verfügbar.



  • weicher schrieb:

    Hier gehts ganz klar um MFC(Visual C++) und daher um Windows.

    Point taken. Das ändert aber nix.



  • Ok, solange mein Programm nicht abstürzt weil zwei Threads gleichzeitig auf eine Variable lesend/schreibend zugreifen, ist alles in Ordnung.

    Veraltete Werte machen mir nicht aus. Dann wird halt erst im nächsten Zyklus aktualisiert.

    Ansonsten synchronisiere ich.

    Danke!


Anmelden zum Antworten