Aufeinander wartende Threads mit pthreads



  • Hallo

    Ich moechte gerne einen Thread implementieren der mehrere Zyklen berechnet sobald die Daten dazu bereit sind. Der Aufruf sieht wie folgt aus:

    Master        Slave
    do_cycle()    warten
    warten        cycle()
    do_cycle()    warten
    warten        cycle()
    

    Wichtig ist hier das der Slave Prozess NICHT jedesmal neu gestartet werden kann und somit nicht einfach die start und join Funktionlitaet genutzt werden kann.

    // wird vom master aufgerufen
    void util::CycleThread::do_cycle() {
      // Is it the first cycle?
      if (first) {
        pthread_mutex_lock(&mutex_cycle);
        start();
        do_a_cycle = false;
        while (!do_a_cycle) { // Needed to avoid "Spirious wakeup"
          pthread_cond_wait(&cond_cycle, &mutex_cycle);
        }
        pthread_mutex_unlock(&mutex_cycle);
        first = false;
      } else {
        pthread_mutex_lock(&mutex_cycle);
        do_a_cycle = false;
        do_a_calc = true;
        pthread_cond_signal(&cond_calc);
        while (!do_a_cycle) {
          pthread_cond_wait(&cond_cycle, &mutex_cycle);
        }
        pthread_mutex_unlock(&mutex_cycle);
      }
    }
    // laeuft im slave thread
    void util::CycleThread::run() {
      init_run();
      while (keeprunning) {
        cycle();
        pthread_mutex_lock(&mutex_calc);
        do_a_cycle = true;
        pthread_cond_signal(&cond_cycle);
        do_a_calc = false;
        while(!do_a_calc){
          pthread_cond_wait(&cond_calc, &mutex_calc);
        }
        pthread_mutex_unlock(&mutex_calc);
      }
      do_a_cycle = true;
      pthread_cond_signal(&cond_cycle);
      end_run();
    }
    

    Der Code laeuft ganz gut aber je nach Auslastung kommt es zu nicht reproduzierbaren Deadlocks. Hat jemand eine Idee wieso?

    Vielen Dank fuer die Hilfe

    Nathan



  • Mal abgesehen das ich vom Nachvollziehen, was da vor sich gehen soll, Kopfschmerzen kriege ...
    fällt mir als erstes auf:

    do_a_cycle = false;
    do_a_calc = true;

    find ich im Master und Slave Thread ???
    sind die variablen fuer beide Seiten die selben ?

    Wenn ja, dann ist sowas :

    while(!do_a_calc){
    

    aeusserst bitter !
    In der schleife aenderst nix an do_a_calc,
    ne zuweissung, auch wenns bool, bzw. 32bit integer ist, ist nicht zwangsläufig atomar. Also musst die auch schuetzen !
    wenn die aber schuetzt, wird der andere thread nie do_a_calc umsetzen koennen, waehrend in der schleife bist !

    while (!do_a_cycle) {
          pthread_cond_wait(&cond_cycle, &mutex_cycle);
    

    Generell! Was soll das bitte sein ? Pollen auf ein Synchronisations - Object ?
    Das ist wie "Vegetarisch kochen mit Rindfleisch" 🙂

    Lass die While-schleifen weg und arbeite generell mit conditions (Events).
    Mutexe fuer die Daten brauchst nicht, wenn ueber conditions steuerst, das master und slave niemals gleichzeitig arbeiten ...

    Wobei sich darufhin die logische Frage zwangslaeufig stellen sollte:
    Wenn master und Slave per Definition niemals gleichzeitig arbeiten, wieso dann bitte multithreading ? Du bekommst nur Nachteile und keinerlei Vorteile durch deine 2 Threads ! Oder was zwingt dich zu, multithreading zu verwenden da ? Oder hasst du mehrere Slaves, dann wuerde dass Sinn machen ...

    Ciao ...



  • ja alle Variablen sind im master und slave Thread die selben.

    RHBaum schrieb:

    while (!do_a_cycle) {
          pthread_cond_wait(&cond_cycle, &mutex_cycle);
    

    Generell! Was soll das bitte sein ? Pollen auf ein Synchronisations - Object ?
    Das ist wie "Vegetarisch kochen mit Rindfleisch" 🙂

    Lass die While-schleifen weg und arbeite generell mit conditions (Events).
    Mutexe fuer die Daten brauchst nicht, wenn ueber conditions steuerst, das master und slave niemals gleichzeitig arbeiten ...

    pthread_cond_wait wacht auf ohne das ein Signal empfangen wurde ("spurious wakeup"). Ist leider in der Theorie und Praxis bei pthreads so. Die Conditions muessen an einen Mutex gebunden werden bei pthreads... Oder meinst du dass ich die locks und unlocks weglassen kann?

    RHBaum schrieb:

    Wobei sich darufhin die logische Frage zwangslaeufig stellen sollte:
    Wenn master und Slave per Definition niemals gleichzeitig arbeiten, wieso dann bitte multithreading ? Du bekommst nur Nachteile und keinerlei Vorteile durch deine 2 Threads ! Oder was zwingt dich zu, multithreading zu verwenden da ? Oder hasst du mehrere Slaves, dann wuerde dass Sinn machen ...

    Der Master fuehrt einen grossen Teil des Codes seriell aus und laesst einen Teil parallel mit OpenMP laufen.

    create_threads();
    
    while(running) {
      serial_part1();
      #pragma omp parallel for
      for(unsigned int i = 0; i < sets; ++i)
        threads[i].do_cycle();
      serial_part2();
    }
    

    in diesem Zyklus wird eine Bibliothek verwendet die an einen Thread gebunden ist (CUDA runtime library). OpenMP grantiert mir aber nicht dass jedesmal der gleiche Thread aus dem Pool entnommen wird. Und so kann es sein dass die Bibliothek dann den falschen Kontext erhaelt. Die OpenMP threads starten also nur einen Zyklus der pthreads Threads und sollten dann warten bis der Zyklus abgeschlossen ist. Wenn jemand eine bessere Idee hat wie das umgesetzt werden kann ohne die Threads zu zerstoeren bin ich da sehr offen fuer.



  • Barriers sind die Loesung:

    void util::CycleThread::run() {
      init_run();
      while (keeprunning) {
        // wait till told to start
        pthread_barrier_wait(&barrier_start);
        cycle();
        // wait till finished
        pthread_barrier_wait(&barrier_end);
      }
      end_run();
    }
    
    void util::CycleThread::do_cycle() {
      // start the cycle
      pthread_barrier_wait(&barrier_start);
      // wait for the cycle to finish
      pthread_barrier_wait(&barrier_end);
    }
    


  • pthread_cond_wait wacht auf ohne das ein Signal empfangen wurde ("spurious wakeup"). Ist leider in der Theorie und Praxis bei pthreads so.

    Neee, dann hasst Du nen ganz anderes Problem ...
    Wuerde ne condition ohne Signal aufwachen, waere sie nutzlos. wenn du sowieso auf richtigkeit pollen musst, dann kannst doch gleich ohne waitcondition pollen und nurn timer einbauen, damit das system ab und an mal luft bekommt ^^
    Also die condition aufwacht ohne Signal -
    Du hasst nen Fehler drinne, oder deine pthread impl ist wertlos. Du musst dich darauf verlassen.

    Wie initialisierst Du denn deine "Barriere" ? Wenn die mit 1 Initialisierst, hasst quasi "sowas" wie ne condition wieder 🙂 Wenn bei dir die Conditions ned laufen, wuerd ich mich aber ned drauf verlassen, dass deine Barrieren laufen.

    Aber dein code mit den barrieren sieht um welten freundlicher aus 🙂
    Ob er dass macht was du willst, wag ich zu bezeifeln ...

    Der Master fuehrt einen grossen Teil des Codes seriell aus und laesst einen Teil parallel mit OpenMP laufen.

    Das heisst, OpenMP "Threads" schiessen bei Dir auf pthread sync Objecte ?
    Informier dich mal fuer deine Plattform, ob OpenMP und pthreads ueberhaupt gleichzeitig drauf laufen ... glaub mal was gelesen zu haben ...

    n diesem Zyklus wird eine Bibliothek verwendet die an einen Thread gebunden ist (CUDA runtime library). OpenMP grantiert mir aber nicht dass jedesmal der gleiche Thread aus dem Pool entnommen wird.

    Das klingt auch recht abenteuerlich. Wenn CUDA will, das seine routinen aus einem Thread gefeuert werden, dannn musst du das sicherstellen klar .
    Das Sollte aber auch mit OpenMP Mitteln gehen. Nur deshalb wuerd ich mir nie pthreads ins boot holen dann (klingt zumindest danach).

    Weiterhin, hasst Du dir mal um die Performance gedanken gemacht ... ich mein Du paralellisierst doch ned Umsonst ?
    Bist sicher das OpenMP Schleifen, die auf Sync Objecte laufen muessen, damit sie wieder nen threadwechsel auf einen Zentralthread machen koennen, schneller laufen als 1 thread, an der Stelle ?
    Man kann Threads auch totsynchronisieren 🙂
    Also testen wuerd ich das auf ale Faelle mal. Threads Performancetechnisch lohnen sich eigentlich nur, wenn eine gewisse Unabhaengigkeit zwischen den threads vorhanden ist.

    Ciao ...



  • Die spurious wakeups sind Realitaet. Ist sogar so dokumentiert. Man muss wirklich pollen. Mir gefaellt das auch nicht aber ist wirklich so. Zumindest in der GNU implementierung von Linux.

    http://pubs.opengroup.org/onlinepubs/007908799/xsh/pthread_cond_wait.html

    Unnuetz ist das aber trotzdem nicht da pollen auf mit signal immernoch besser ist als ein spinlock oder pollen mit sleep.

    Die Barriers sind natuerlich mit 2 intialisiert. Sonst bringt das ja nix 🙂

    Mit dem OpenMP und pthreads gleichzeitig gab es keine Probleme bis jetzt. Hab dazu auch nichts gefunden. Es waer aber nicht soooo ein riesen Aufwand alles mit pthreads umzuschreiben und openmp ganz zu kicken. brauchen eh nur einen einzigen parallel for.

    Dass das mit OpenMP gehen sollte fand ich auch. Ich konnte dazu aber nichts finden. Standardmaessig wurde der Cuda context entgegen meiner Vermutung zerstoert was auf eine Beendigung des Threads hinweist.

    Die Performance ist sehr gut so wie sie ist. In der alten implementierung hatte wir gar keine pthreads und nur OpenMP und der code war nicht schneller. Das liegt aber daran dass jeder OpenMP thread wirklich SEHR viel rechnet.

    LG,
    Nathan



  • Wieso haben die die spurious wakeups nicht in einem "BUGS" Abschnitt drin?
    Also für mich klingt das nach Bug.

    Wuerde ne condition ohne Signal aufwachen, waere sie nutzlos. wenn du sowieso auf richtigkeit pollen musst, dann kannst doch gleich ohne waitcondition pollen und nurn timer einbauen, damit das system ab und an mal luft bekommt ^^

    Naja bei dieser Lösung wird halt nur bei den spurious wakeups neu gepollt (die ja hoffentlich nur eine Ausnahme sind). ich würde tippen dass die Lösung des OP da besser ist



  • Vor allem was ich gar nicht verstehe, ist warum fasst das die pthread impl eh ned selber ab ...
    Den Signal status setzt du über ne pthread funktion, das warten erledigt eine pthread funktion, die pthread hat also alles unter kontrolle ! ob sie selber noch die bedingung ueber ne weitere variable absichert und damit die spurious wakeups verhindert, wuerde der user am interface gar ned mitbekommen ...
    Das sollt weder nen Performanceproblem sein noch nen "Feature" verlust, glaub kein User kann mit den spurious wakeups was anfangen. Stattdessen zerschiessens lieber den Code des Users ...

    Was auch komisch ist, hab 3 Buecher ueber Linux mit mehr oder weniger grossen pthreads-teil. In allen werden conditions behandelt und Beispiele gezeigt. In keinem wird das "spurious wakeups" Problem erwaehnt oder gar in nem Beispiel abgefasst ! die beispielle duerften zumindest alle unerwartete effekte hervorbringen wenn es passiert.

    Glaub ich versteh langsam warum viele Linuxer pthreads ned wirklich mögen.
    Statt das Problem umgehen (workaround) wuerd ich lieber das Problem ganz eleminieren, und schauen, ob ne Lib die drueber aufsetzt, vielleicht keine so probleme hat (weils das selber haendelt)
    boost::thraeds vielleicht.

    Oder komm auf die dunkle Seite der Macht !
    "WaitForSingleObject" funktioniert so wie man es erwartet, ohne hacks 🤡

    Hier noch paar Hintergruende ....
    http://stackoverflow.com/questions/1136371/pthread-and-wait-conditions

    Problem ist also das nen Signal den wakup hervorrufen kann.
    Ich find immer noch das es in der Lib haendelbar waer.
    Zumal auch nicht garantiert wird, das du einen wakeup bekommst, wenn dein Prozess nen Signal bekommt. Das ganze ist irgendwie alles andere als nachvollziehbar dann ...

    Ciao ...


Anmelden zum Antworten