Threads Synchronisieren auch bei Lese-Zugriff?



  • CStoll schrieb:

    Und woher weiß ein lesender Thread jetzt, ob irgendwann während seiner Arbeit ein anderer Thread kommen und schreiben wird? Dein Computer kann nicht in die Zukunft sehen, deshalb muß er vom schlimmsten Fall ausgehen - wenn es möglich ist, die Datenstruktur zu ändern, wird auch jemand das aus einem anderen Thread heraus tun.

    Das weiß der Thread nicht - das muss der Programmierer schon selbst wissen 😉



  • Gut, dann muß halt der Programmierer in die Zukunft sehen können - bzw. sehr genau wissen, wann und von wo aus die Daten geschrieben werden könnten.
    (Beispiel: Wenn du die Grundeinstellungen einmal zusammenbaust, bevor du deine Arbeits-Threads startest (die dann grundsätzlich nur lesend zugreifen), brauchst du keine Synchronisation. Wenn du irgendwann auf die Idee kommst, diese Einstellungen im laufenden Betrieb zu ändern, mußt du dafür sorgen, daß der Options-Bearbeiten-Thread nicht mit den Arbeits-Threads kollidiert)



  • Richtig.



  • Ein kleiner Denkanstoß:

    bei java ist es so, dass man bei änderbaren sachen selbst wenn es sich nur um einen integer handelt man beim lesen auch synchronisieren muss (in form von volatile, synchronized (eigentlich das gleich wie lock) oder lock().

    Der Grund ist, dass bei java wenn man keine der 3 varianten verwendet kein memory-refresh durchgeführt wird, und änderungen möglicherweise nicht sichtbar werden.

    Gibt es diese Problematik hier auch, oder ist das irgendwie anders gelöst? Z.B. könnten automatisch immer memory-flushs und memory-refreshs durchgeführt werden, was aber wohl zu performanceeinbußen führen würde, außer wenn der compiler irgendwie herausfinden kann, welche daten gemeinsam verwendet werden und welche nicht...



  • "Don't pay for what you don't use" lautet das Prinzip im wunderbaren C++ Land 😃



  • Gast1337 schrieb:

    bei java ist es so, dass man bei änderbaren sachen selbst wenn es sich nur um einen integer handelt man beim lesen auch synchronisieren muss (in form von volatile, synchronized (eigentlich das gleich wie lock) oder lock().

    Der Grund ist, dass bei java wenn man keine der 3 varianten verwendet kein memory-refresh durchgeführt wird, und änderungen möglicherweise nicht sichtbar werden.

    Die Speicher-Sichtbarkeit spielt nur dann eine Rolle, wenn etwas irgendwann irgendwo geändert wird. Wenn es immer schon so war, und immer so bleibt, dann braucht man auch in Java nichts spezielles zu machen damit Lesen OK ist.

    Da Threads starten und joinen implizite Full-Barriers sind, ist das auch kein rein theoretischer Fall.
    z.B. wenn du eine Datenstruktur anfüllst, und dann 10 Threads startest die die Daten darin lesen (und nur lesen), dann musst du nichts synchronisieren. Vorausgesetzt die Datenstruktur wird auch sonst nirgends geändert - zumindest nicht bevor alle Threads wieder gejoined wurden.

    Das ist mit "bei nur Lesen muss man nichts synchronisieren" gemeint.

    Gibt es diese Problematik hier auch, oder ist das irgendwie anders gelöst? Z.B. könnten automatisch immer memory-flushs und memory-refreshs durchgeführt werden, was aber wohl zu performanceeinbußen führen würde, außer wenn der compiler irgendwie herausfinden kann, welche daten gemeinsam verwendet werden und welche nicht...

    C++ garantiert was das angeht noch viel weniger als Java. z.B. ist das Lesen einer "volatile" Variable in C++ kein "load_acquire" und das Schreiben kein "store_release". (In Java dagegen schon).

    Also ja: die Problematik gibt es in C++ auch. Allerdings gilt auch in C++ das "bei nur Lesen muss man nichts synchronisieren" so wie ich es oben beschrieben habe.



  • Ok, das mit dem "nur Lese-Zugriff" hatte ich dann falsch verstanden.

    "load_acquire" und "store_release" sagt mir leider nichts, aber ich werds gleich mal googlen.

    Aber nochmal zu den garantien bei C++:
    Ich hab gelesen, dass C++ sequentielle Konsistenz garantiert.

    The C++0x draft ensures sequential consistency for programs that do not explicitly specify weaker memory ordering constraints, which avoids data races (concurrent non-read-only accesses to the same memory location).

    Quelle: http://en.wikipedia.org/wiki/C%2B%2B0x#Multitasking_memory_model
    Das bedeutet, so wie ich das verstehe , dass es niemals sichbarkeitsprobleme geben kann, also das bei jedem schreibzugriff, der von einem anderen thread gelesen werden könnte ein memory flush ausgeführt wird und bei jedem lesezugriff sofern eine änderung durch einen anderen thread möglich war ein memory refresh ausgeführt wird.
    Natürlich heißt das nicht, dass man keine locks mehr braucht.
    Das wären dann wesentlich stärkere garantieren als bei java, das wäre ja so, als wenn wirklich JEDE variable volatile wäre.

    "Don't pay for what you don't use" lautet das Prinzip im wunderbaren C++ Land

    Dann würde dieses zitat auch etwas hinfällig werden...
    Ich kann das auch nicht wirklich nachvollziehen, ich hatte es sonst immer so wahrgenommen, dass C(++) meistens so gebaut ist, dass hohe geschwindgkeit erreicht wird und das bei java eher sekundär ist, was hierbei dann ja genau umgedreht ist...

    Habe ich da was falsch verstanden?
    Bitte kommentieren 🙂



  • Ich wollte damit sagen, dass in C++ nichts von Standard verlangt wird, um Thread-Safety zu garantieren. Das ist eben das C++-Prinzip: Wenn ich nicht mehrere Threads brauche, dann habe ich auch keinen Performancenachteil.



  • Ok, würde mich freuen, wenn noch jemand das mit der sequentiellen konsistenz kommentieren kann.
    Also folgende Sachen:
    - Hab ich das richtig verstanden (siehe letzte Seite)
    - Warum wird sowas bei C++ garantiert, wenn das nichtmal bei java der fall ist?
    - Wirkt sich das nicht stark negativ auf die performance aus, oder können die compiler irgendwie rausfinden (stichwort escape analyse und co) was genau gemeinsam verwendet wird und dann nur dafür die flushs einfügen?



  • Gast1337 schrieb:

    Aber nochmal zu den garantien bei C++:
    Ich hab gelesen, dass C++ sequentielle Konsistenz garantiert.

    The C++0x draft ensures sequential consistency for programs that do not explicitly specify weaker memory ordering constraints, which avoids data races (concurrent non-read-only accesses to the same memory location).

    Quelle: http://en.wikipedia.org/wiki/C%2B%2B0x#Multitasking_memory_model
    Das bedeutet, so wie ich das verstehe , dass es niemals sichbarkeitsprobleme geben kann, also das bei jedem schreibzugriff, der von einem anderen thread gelesen werden könnte ein memory flush ausgeführt wird und bei jedem lesezugriff sofern eine änderung durch einen anderen thread möglich war ein memory refresh ausgeführt wird.

    Ich hab den aktuellen Draft nicht gelesen, aber nach dem was ich in den letzten Monaten so mitbekommen habe gilt das nur für atomic<T> Variablen (bzw. für die diversen atomic_xxx Funktionen).
    Ähnliche war es auch eine Zeit lang für volatile Variablen geplant (bzw. im Gespräch), aber letztens hat man mich hier im Forum darauf hingewiesen dass es für volatile nicht in den Draft übernommen wurde.

    Für sämtliche Speicherzugriffe wäre es auch komplett plem, da es auf einigen Plattformen (CPUs) jedes C++ Programm auf Schneckentempo verlangsamen würde.



  • 314159265358979 schrieb:

    Ich wollte damit sagen, dass in C++ nichts von Standard verlangt wird, um Thread-Safety zu garantieren. Das ist eben das C++-Prinzip: Wenn ich nicht mehrere Threads brauche, dann habe ich auch keinen Performancenachteil.

    Das stimmt so nicht ganz.

    Im neuen Standard wird für viele Funktionen threads-safety vorgeschrieben sein.
    Vermutlich für so ziemlich alle globalen Funktionen. Also z.B. auch ::operator new und ::operator delete.

    Da eine Heap-Implementierung nicht gratis thread safe gemacht werden kann, zahlt man bei Programmen ohne Threads also sehrwohl dafür, obwohl man es nicht verwendet.

    Das ist eben das C++-Prinzip

    Ja, C++ versucht dieses Prinzip umzusetzen, schafft es aber an vielen Stellen nicht.



  • Wider was dazu gelernt 🙂



  • hustbaer schrieb:

    Im neuen Standard wird für viele Funktionen threads-safety vorgeschrieben sein.
    Vermutlich für so ziemlich alle globalen Funktionen. Also z.B. auch ::operator new und ::operator delete.

    Da eine Heap-Implementierung nicht gratis thread safe gemacht werden kann, zahlt man bei Programmen ohne Threads also sehrwohl dafür, obwohl man es nicht verwendet.

    Verstehe ich es richtig, dass in Nicht-Multi-Threading-Programmen eine (unnötige) Synchronisierung beim Anfordern von Speicher gemacht wird?
    Ich hätte eigentlich erwartet, dass bei der Übergabe des Parameters pthread erst Threadsicherheit in die Speicheranforderung (und andere Funktionen) integriert wird... 😮
    Klar ist es wahrscheinlich Compiler-abhängig, aber bei guten Compilern würde ich es leichtsinnigerweise(?) annehmen

    EDIT: Ganze Seiten müssen natürlich beim OS angefragt werden, die dann natürlich wegen anderen Prozessen synchronisiert werden müssen.



  • Es ist möglich, dass der neue Standard es erlauben wird, dass die thread-safety der Standard-Library "opt out" ist. "opt in" halte ich für ganz verkehrt, und ich hoffe dass der neue Standard das auch nicht erlauben wird.

    Wie es bei GCC und Linux ist kann ich nicht sicher sagen. Ich schätze dass da auch per Default alles thread-safe. Was MSVC angeht: ab VS 2010 gibt es keine single-threaded Runtime mehr.

    ps:

    Verstehe ich es richtig, dass in Nicht-Multi-Threading-Programmen eine (unnötige) Synchronisierung beim Anfordern von Speicher gemacht wird?

    Sobald die Runtime als DLL oder SO vorliegt, kann man nicht mehr so einfach sagen ob das Synchronisieren unnötig ist oder nicht. Angenommen du verwendest Library X, und Library X verwendet intern Threads (ohne dass du was davon mitbekommst). Und Library X ist gegen die selbe Runtime gelinkt. Dann muss synchronisiert werden. "Library X" kann dabei auch Teil des Betriebssystems sein. (Bei Windows weniger, da das OS nicht die Visual Studio Runtime verwendet, aber bei Linux ist es soweit ich weiss ganz normal dass "alles" (vieles) im System die selbe Runtime-Library, und damit auch den selben Heap verwendet)



  • hustbaer schrieb:

    Sobald die Runtime als DLL oder SO vorliegt, kann man nicht mehr so einfach sagen ob das Synchronisieren unnötig ist oder nicht.

    Stimmt 👍


Anmelden zum Antworten