Multithreading



  • rthysd schrieb:

    Julian__ schrieb:

    zuum thema threads/multithreading hätte ich auch noch eine frage:
    ich habe schon einiges gelesen, aber es nie wirklich verstanden. ich weiss im prinzip was threads sind, was single- und multithreading ist, hätte aber gerne nochmal eine anfängerfreundliche erklärung der in dem zusammenhang gängigen begriffe. ich habe z.B. bis jetzt aus keiner erklärung verstanden, was mutexe, semaphores, atomaren typen/operationen, ... sind. also wäre nett wenn jemand z.B. einen link für mich hätte, in dem das thema threads verständlich erklärt wird.

    mfg,
    julian

    Wenn du einen Thread hast, dann läuft alles schön der Reihe nach ab. Eine Funktion nach der anderen. Wenn du in deinem Programm noch einen Thread startest (so ähnlich, wie ein Programm, dass in deinem Programm läuft), dann kannst du zwei oder mehrere Funktionen gleichzeitig abarbeiten lassen. Semaphre, Mutex.. sind dazu da, dass man den Zugriff auf speicher reglen kann. Ein Thread 1 sperrt sich sozusagen einen Speicherbereich, damit der andere Thread 2 nicht reinschreibt, während er 1 drin liest, oder anders rum.

    danke, aber so viel wusste ich schon. genauere erklärung für mutex und semaphore, wo die unterschiede sind, was genau sie machen etc. waäre jetzt z.B. gut.
    außerdem: was sind atomare operationen und typen?

    mfg,
    julian



  • Kurz und knapp:
    Semaphore dient dazu, die Anzahl von Threads für ein shared Object zu begrenzen. Man kann also sagen, dass 4 Threads gleichzeitig etwas machen dürfen aber ein 5. wartenmuss, bis eines der 4 Threads diese Resource verlassen hat.

    Mutex ist ein Prozessübergreifendes Objekt (heißt also, man kann aus verschiedenen Programmen darauf zugreifen) und dient zur Synchronisation. Ein Mutex hat aber nur 2 zustandsgrößen - entweder Signaled oder Non-Signaled.

    Dann gibt es noch Critical Sections. Diese sind nur Thread übergreifend (also nicht Prozessübergreifend) und verhalten sich wie Mutexe. Nur verbraucht das setzen eines Critical Sections weniger CPU Cycles als bei Mutexen.

    Atomare Typen sind Datentypen, dessen Änderung (also Schreiben) in einem einzigen CPU Cycle erfolgt. Unter 32 Bit Systemen (bei Windows) wären das 32 bit Integers. (bei char, short, float etc... wird das nicht garantiert). Bei einem Double braucht man unter Umständen mehrere Cycles. Was hat das mit Threads zu tun? Angenommen man hat 2 Threads, der eine schreibt dauernd einen Wert und der andere liest diesen aus. Wenn man mit atomaren Typen arbeitet, muss man theoretisch keine Synchronisation ausüben und der lesende Thread wird nie "Datenmüll" lesen (aber wird auch nicht mit dem schreibenden Thread synchron laufen). Ob das elegant ist, ist natürlich eine andere Frage.

    Analog dazu sind atomare Operationen Operationen, die in einem CPU Cycle erledigt werden. Somit ist das Lesen ODER das Schreiben eines normalen ints eine atomare Aktion. Übrrigens:

    ++i;
    

    ist nicht mehr atomar, da es das Lesen des Wertes und das Schreiben des Wertes erfordert.



  • Atomare Typen sind Datentypen, dessen Änderung (also Schreiben) in einem einzigen CPU Cycle erfolgt. Unter 32 Bit Systemen (bei Windows) wären das 32 bit Integers. (bei char, short, float etc... wird das nicht garantiert). Bei einem Double braucht man unter Umständen mehrere Cycles. Was hat das mit Threads zu tun? Angenommen man hat 2 Threads, der eine schreibt dauernd einen Wert und der andere liest diesen aus. Wenn man mit atomaren Typen arbeitet, muss man theoretisch keine Synchronisation ausüben und der lesende Thread wird nie "Datenmüll" lesen (aber wird auch nicht mit dem schreibenden Thread synchron laufen). Ob das elegant ist, ist natürlich eine andere Frage.

    Analog dazu sind atomare Operationen Operationen, die in einem CPU Cycle erledigt werden. Somit ist das Lesen ODER das Schreiben eines normalen ints eine atomare Aktion.

    Woher willst du wissen, ob der Andwender eine CPU hat, die überhaupt atomare Typen/Operationen hat? Ein 32 bit Integer kann auf der einen CPU atomar sein, auf einer anderen wieder nicht. Obwohl die Andwendung unter dem gleichen Betriebssystem läuft.

    Aber btw, mal was anderes:
    Können Multicore CPUs Threads implizit aufteilen? Also eine Singlethread-Anwendung in eine MT-Anwendung verwandeln, so das alle Kerne möglichst optimal ausgelastet werden?



  • "Ein 32 bit Integer kann auf der einen CPU atomar sein"

    Ja, auf einem 32 Bit CPU. Ich bin jetzt von einer x86 Architektur ausgegangen. Mit anderen Archtiekturen kenne ich mich nicht aus, insofern bezieht sich meine Antwort auf aktuelle PC Technologie.

    Was das Aufteilen eines Threads geht: Das geht nicht, zumindest nicht bei Verwendung mit einem standard Compiler - das bezieht sich wieder auf Windows. Ob andere BS das können, kann ich nicht sagen. Ich bezweifle es aber sehr stark!



  • pub00515 schrieb:

    "Ein 32 bit Integer kann auf der einen CPU atomar sein"

    Ja, auf einem 32 Bit CPU. Ich bin jetzt von einer x86 Architektur ausgegangen. Mit anderen Archtiekturen kenne ich mich nicht aus, insofern bezieht sich meine Antwort auf aktuelle PC Technologie.

    Wenn du schon von aktueller PC-Technologie sprichst, dann musst du ja auch Multicore CPUs einbeziehen. Dann ist deine Operation aber nicht atomar, da die Threads wirklich parallel laufen. (Das gleiche Problem natürlich bei SMP oder geclusterten Anwendungen).

    Wie gesagt: Shared State Threading ist sehr kompliziert! Entweder man macht es falsch, weil man nicht richtig locked oder man macht es falsch, weil am Ende alle Threads eh nur auf Mutexe warten oder gar beides. Message basiertes Threading ist deutlich leichter, aber leider weniger populär.



  • "Wenn du schon von aktueller PC-Technologie sprichst, dann musst du ja auch Multicore CPUs einbeziehen. Dann ist deine Operation aber nicht atomar, da die Threads wirklich parallel laufen."

    Na, bezogen auf den Kern, ist die Operation schon atomar. Wenn man es auf die Zeit bezieht, ist es nicht mehr atomar, da hast du Recht. Jetzt ist die Frage, wie atomare Operationen definiert sind. Im Prinzip ist es ja auch wurscht, denn darauf sollte man sich eh nicht verlassen...



  • ok, vielen vielen dank pub00515 !
    ich habe schon einiges gelesen, wäre dabei aber nie auf die idee gekommen, das was du geschrieben hast daraus zu interpretieren (wusste aber auch nicht wie ich es überhaupt interpretieren soll).
    also jetzt habe ich es endlich einmal verstanden. die anwendung ist dann eine andere sache, aber wenn ich mich jetzt mithilfe von ein paar tutorials einarbeite, werde cih das schon hinbekommen.

    also nochmals vielen dank!!

    mfg,
    julian



  • rüdiger schrieb:

    Message basiertes Threading ist deutlich leichter, aber leider weniger populär.

    weniger polulär?
    z.b. der windows fenstermanager benutzt message based threading...
    vor allen geht das auch ohne taskswitching (im sinne von cpu register und stackpointer umschalten).



  • pub00515 schrieb:

    Mutex ist ein Prozessübergreifendes Objekt (heißt also, man kann aus verschiedenen Programmen darauf zugreifen) und dient zur Synchronisation. Ein Mutex hat aber nur 2 zustandsgrößen - entweder Signaled oder Non-Signaled.

    Nee. Mutex ist ein binaerer Semaphor. Nichts weiter.

    pub00515 schrieb:

    Dann gibt es noch Critical Sections. Diese sind nur Thread übergreifend (also nicht Prozessübergreifend) und verhalten sich wie Mutexe. Nur verbraucht das setzen eines Critical Sections weniger CPU Cycles als bei Mutexen.

    WTF? Critical section ist der Grund, warum das ganze ueberhaupt ein Problem ist. Wie der Name schon sagt, ist es ein kritischer Abschnitt. Kritischer Abschnitt des Programms, welches der Prozess ausfuehrt.

    pub00515 schrieb:

    Atomare Typen sind Datentypen, dessen Änderung (also Schreiben) in einem einzigen CPU Cycle erfolgt. Unter 32 Bit Systemen (bei Windows) wären das 32 bit Integers. (bei char, short, float etc... wird das nicht garantiert). Bei einem Double braucht man unter Umständen mehrere Cycles. Was hat das mit Threads zu tun? Angenommen man hat 2 Threads, der eine schreibt dauernd einen Wert und der andere liest diesen aus. Wenn man mit atomaren Typen arbeitet, muss man theoretisch keine Synchronisation ausüben und der lesende Thread wird nie "Datenmüll" lesen (aber wird auch nicht mit dem schreibenden Thread synchron laufen). Ob das elegant ist, ist natürlich eine andere Frage.

    Atomar bedeutet unteilbar. Im Zusammenhang mit MT habe ich noch nie jemanden von atomaren Typen sprechen hoeren. Nur von atomaren Operationen.

    pub00515 schrieb:

    Analog dazu sind atomare Operationen Operationen, die in einem CPU Cycle erledigt werden. Somit ist das Lesen ODER das Schreiben eines normalen ints eine atomare Aktion. Übrrigens:

    ++i;
    

    ist nicht mehr atomar, da es das Lesen des Wertes und das Schreiben des Wertes erfordert.

    Auch nicht. Wie bereits geschrieben, heisst atomar unteilbar. In diesem Zusammenhang also nicht unterbrechbar. Und AFAIK ist ++i atomar, i++ ist es nicht.



  • Apollon schrieb:

    pub00515 schrieb:

    Mutex ist ein Prozessübergreifendes Objekt (heißt also, man kann aus verschiedenen Programmen darauf zugreifen) und dient zur Synchronisation. Ein Mutex hat aber nur 2 zustandsgrößen - entweder Signaled oder Non-Signaled.

    Nee. Mutex ist ein binaerer Semaphor. Nichts weiter.

    pub00515 schrieb:

    Dann gibt es noch Critical Sections. Diese sind nur Thread übergreifend (also nicht Prozessübergreifend) und verhalten sich wie Mutexe. Nur verbraucht das setzen eines Critical Sections weniger CPU Cycles als bei Mutexen.

    WTF? Critical section ist der Grund, warum das ganze ueberhaupt ein Problem ist. Wie der Name schon sagt, ist es ein kritischer Abschnitt. Kritischer Abschnitt des Programms, welches der Prozess ausfuehrt.

    pub0515 sieht das ganze vom standpunkt eines win32-coders.
    Apollon hält sich mehr an die allgemeine sichtweise.
    also habt ihr beide recht 😉



  • pub00515 schrieb:

    Eine Performancesteigerung erreichst du nur, wenn du mehrere Prozessoren oder Kerne hast. Bei nur einem Kern dürftest du sogar ein bisschen weniger "Umsatz" erzielen, da Windows (?) ständig hin und her schalten muss (zwischen den Threads).

    Laut Bruce Eckel (Autor von Thinking in C++ & Thinking in Java) bringt Multithreading praktisch häufig auch auf 1-Prozessor-Systemen leicht bessere Performance.

    Keine Ahnung obs stimmt, aber der Typ sollte eigentlich wissen, wovon er spricht. 🤡



  • byto schrieb:

    Laut Bruce Eckel (Autor von Thinking in C++ & Thinking in Java) bringt Multithreading praktisch häufig auch auf 1-Prozessor-Systemen leicht bessere Performance.

    ich würde eher sagen leicht schlechtere.
    das task switching selber verbrät ja auch rechenleistung.
    beste performance hat man, wenn die cpu ungehindert rennen kann, ohne interrupts usw...
    🙂



  • Kommt halt auf die Operationen an und wie diese die CPU auslasten. Wenn Du lediglich Berechnungen durchführst, ohne dass die Operation irgendwann blockt (z.B. durch I/O etc.), dann hast Du natürlich keine Performance-Steigerung. Aber sobald die Operation blockt, kannst Du eben Leistung steigern, indem die Kontrolle dann an einen zweiten Thread geht, um somit die CPU besser auszulasten.



  • Nochmal zu Atomaren Operationen. Da gibts in der Windows API die sogenannten Interlocked..... Funktionen mit denen kann man Inkrement/Dekrement/Addieren.

    So:

    volatile long blub;
    InterlockedIncrement(blub);
    

    Mit Hilfe davon kann man sich dann wiederum eigene Locks (Critical Sections) basteln. ⚠ Auf Linux stehen dir dann wahrscheinlich wieder andere APIs dazu zur Verfügung.

    Wenn du wissen willst wie Multi Threading im Detail und der Theorie funktioniert dann kann ich das Buch Modern Multithreading empfehlen. (Das ist allerdings sehr langweilig, englisch und formatierung ist auch nicht vorhanden 😉 )

    Mich würde mal interessieren ob Critical Sections unter Windows das Bound Waiting Prinzip umsetzen. dh. das ein Thread der am längsten wartet auch als nächstes dran kommt. 😕



  • ten schrieb:

    byto schrieb:

    Laut Bruce Eckel (Autor von Thinking in C++ & Thinking in Java) bringt Multithreading praktisch häufig auch auf 1-Prozessor-Systemen leicht bessere Performance.

    ich würde eher sagen leicht schlechtere.
    das task switching selber verbrät ja auch rechenleistung.
    beste performance hat man, wenn die cpu ungehindert rennen kann, ohne interrupts usw...
    🙂

    kommt wohl drauf an, ob man vorher ein Rechner hatte auf dem das Ding exklusiv lief oder ein Rechner hatte, auf dem eh schon ein Betriebssystem war, vielleicht sogar eines, was ständig GUI und N andere Programme laufen lässt 🙂

    Aber Threading bietet eben den Vorteil, das die Anwendung besser skaliert, wenn man weitere Rechner oder CPUs hinzufügt.



  • Aber Threading bietet eben den Vorteil, das die Anwendung besser skaliert, wenn man weitere Rechner oder CPUs hinzufügt.

    Das kommt, wie schon hier gesagt, auf den Algorithmus an. Devide&Conquer-Algos profitieren am meisten von MT, aber andere, in denen ein Ergebnis auf einen anderen aufbaut profitieren überhaupt nicht davon.



  • DEvent schrieb:

    Aber Threading bietet eben den Vorteil, das die Anwendung besser skaliert, wenn man weitere Rechner oder CPUs hinzufügt.

    Das kommt, wie schon hier gesagt, auf den Algorithmus an. Devide&Conquer-Algos profitieren am meisten von MT, aber andere, in denen ein Ergebnis auf einen anderen aufbaut profitieren überhaupt nicht davon.

    Das ein Algorithmus den man nicht in mehrere Threads aufteilen kann von Threads nicht profitiert, dürfte ja selbst redend sein...



  • Apollon schrieb:

    Und AFAIK ist ++i atomar, i++ ist es nicht.

    Das ist wohl egal. Entscheidend ist wohl eher, ob i in nem register steht oder nicht. Wenn du eine einfache for schleife hast, wird der assembler code wohl immer

    inc cx
    

    sein, egal ob du i++ oder ++i schreibst. Wenn die Schleife komplizierter ist und auch mal was anderes als i in cx steht, so das i von speicher gelesen werden muss, dann ist es nicht mehr atomar



  • Hm was mich mal interessieren würde:

    Gehen wir mal von folgender Situation aus 2 Bitmaps (800x600) die ineinander geblendet werden sollen. Währe ja ein einfach realisierbares Multithreading Szenario.

    Ab wann lohnt sich Multithreading (Ist eventuell die benötigte Zeit um die Threads starten zu lassen höher als es in einem zu Berechnen). Bzw sollte man die Thread dauerhaft laufen lassen und ihnen ihre Aufgaben zu kommen lassen.

    Wie bestimmt man wieviel Threads sich lohnen für eine Rechenoperation mit 800x600 Schleifendurchläufen. Also das von Multi Threading profitiert wird und nicht der Verwaltungsaufwand zu groß wird. (Hängt wahrscheinlich auch von Anzahl CPUs ab also muss die ja auch mit einbezogen werden)

    mfg



  • Mit dem Satz "Hängt wahrscheinlich auch von Anzahl CPUs ab also muss die ja auch mit einbezogen werden" wäre ich vorsichtig. Bei gängigen Betriebssystemen bekommt nur dein Prozess die Rechenzeit zugeteilt und dieser verteilt diese wiederum an die entsprechenden Threads. Es gibt aber auch Betriebsysteme (z.B. irgendwelche Serverbetriebssysteme) die "schwergewichtige Threads" (die genaue Bezeichnung weis ich jetzt nicht mehr) unterstützen und den einzelnen Threads direkt Prozessorzeit zuweisen.

    Ich bin allerdings erst dabei mich in die Materie einzuarbeiten. Darum kanns sein das mein Post nicht 100%ig vollständig ist (man möge mich berichtigen 😃 ).


Anmelden zum Antworten