thread problem gemeinsamer speicherbereich



  • Hi,

    ich habe einige fragen zu threads da ich noch nicht ganz verstanden habe wie
    der speicher zwischen den threads geteilt wird.

    wenn ich nun zwei threads starte sind alle variablen in *count einem
    thread zugeordnet d.h. sind die variablen lokal oder global für alle
    threads?

    die threads sollen von einer übergebenen zahl rückwerts zählen nur irgendwas
    mache ich hier falsch. weil wenn ich das ganze z.b mit

    programm 4 7 starte zählen beide threads von 7 bis 0.

    gibt es eine möglichkeit für threads einen lokalen speicherbereich
    anzulegen?

    lg

    #include <stdio.h>
    #include <stdlib.h>
    #include <pthread.h>
    #include <time.h>
    struct thread_param 
    {
        int x;
    };
    
    void *count(void *arg)
    {
      int n = (((struct thread_param*)arg))->x;
    
      while(n > 0)
      {
        sleep(1);
        n--;
        printf("%d\n",n);    
      }
    	return(0);
    
    }
    
    int main(int argc, char **argv)
    {
      char *message1 = "Thread 1";
      int i;
      pthread_t threads[100];
    
      struct thread_param *tp;
      if((tp = malloc(sizeof(*tp))) == NULL)
      {
        fprintf(stderr,"MALLOC THREAD_PARAM ERROR");
        return (-1);
      }
    
      for(i = 0;i < argc - 1; i++)
      {
        tp->x = atoi(argv[i+1]);
        pthread_create(&threads[i],NULL,count,(void*)tp); 
      }
      i = 0;
      for(i = 0;i < argc - 1; i++)
      {
        pthread_join(threads[i],NULL);
      }
    
    }
    


  • Jeder Thread hat seinen eigenen Stack.
    Andere Speicherbereiche wie Text, Data und BSS sind gleich.

    Also wenn du eine gemeinsame Variable haben willst mach sie global, oder am Heap, oder in main und gib jedem Thread die Adresse darauf mit.

    int x=10; // im gemeinsamen Datenbereich aller Threads
    
    int main()
    {...}
    
    int aThreadFunction()
    {
    ++x; // es teilen sich nun alle Threads die gleiche Variable x
    }
    
    const int x=10; // im gemeinsamen Datenbereich aller Threads
    
    int main()
    {...}
    
    int aThreadFunction()
    {
    int localX=x; // liegt am Stack, wovon jeder Thread seinen eigenen hat
    ++localX; // nur im eigenen Thread sieht man die Veränderung von localX
    }
    

    Wenn du mehr über Threads in Linux wissen möchtest, such nach "Light-Weight Process". Threads sind nichts anderes als Prozesse, allerdings teilen sie sich viele Ressourcen (eben beispielsweise den virtuellen Adressraum) mit den anderen Threads, die du in deinem Programm verwendest.


  • Mod

    Threadübergreifend: Statische Variablen; Globale Variablen; Sachen die mit malloc angelegt wurden; Literale und ähnliches
    Threadlokal: Alles andere. (Insbesondere auch statische und globale Variablen, die speziell threadlokal gemacht wurden. Sollte in Anleitungen zu Pthreads erklärt sein)

    Das heißt, jeder der Threads hat zwar eine Kopie des Zeigers tp, aber der Speicher der in Zeile 32 mit malloc angefordert wurde ist allen Threads gemeinsam. Du suchst wohl eher etwas in dieser Richtung:
    (ungetestet)

    #include <stdio.h>
    #include <stdlib.h>
    #include <pthread.h>
    #include <time.h>
    struct thread_param
    {
        int x;
    };
    
    void *count(void *arg)
    {
      int n = (((struct thread_param*)arg))->x;
    
      while(n > 0)
      {
        sleep(1);
        n--;
        printf("%d\n",n);    
      }
        return(0);
    
    }
    
    int main(int argc, char **argv)
    {
      int i;
      pthread_t threads[100];
      struct thread_param tp[100];
    
      for(i = 0;i < argc - 1; i++)  
      {
        tp[i].x = atoi(argv[i+1]);
        pthread_create(&threads[i],NULL,count, tp+i);
      }
    
      for(i = 0;i < argc - 1; i++)
      {
        pthread_join(threads[i],NULL);
      } 
    }
    


  • Zudem solltest du bedenken, dass ein Speicherbereich evtl. abgesichert werden muss ( z.b. durch CriticalSection ) wenn du gedenkst, schreibend mit einem oder mehreren Threads auf den Speicher einzuwirken.



  • hi,

    erstmal danke für eure antworten. also mir gehts gerade darum dass ich
    keine gemeinsamen variablen haben möchte da jeder thread unabhängige
    berechnungen durchführen soll.

    das mit dem gemeinsamen speicher wegen malloc hab ich mir gedacht.
    nur weise ich ja den wert im thread der lokalen variable n zu.
    müsste n jetzt nicht unterschiedliche werte haben in jedem thread da ja
    n lokal ist?

    int n = (((struct thread_param*)arg))->x;
    


  • n hat genau den Wert, den du ihm in pthread_create als thread_param-struktur übergibst.



  • ja aber dann versteh ich die ausgabe nicht.
    wenn ich das ganze starte mit ./programname 2 9

    sollten 2 threads gestartet werden. einer soll von 2 herunter zählen und
    einer von 9. die ausgabe ist aber :

    Thread 764876544: 8
    Thread 773269248: 8
    Thread 764876544: 7
    Thread 773269248: 7
    Thread 764876544: 6
    Thread 773269248: 6
    Thread 773269248: 5
    Thread 764876544: 5
    Thread 773269248: 4
    Thread 764876544: 4
    Thread 773269248: 3
    Thread 764876544: 3
    Thread 773269248: 2
    Thread 764876544: 2
    Thread 773269248: 1
    Thread 764876544: 1
    Thread 773269248: 0
    Thread 764876544: 0

    also alle beide zählen von 9 herunter. oder hab ich da wo einen anderen
    fehler?



  • in deinen Beispiel gibst du in pthread_create keinen Wert, sondern einen Zeiger auf einen Wert. pthread_create ist asynchron, d.h. wenn die Funktion zurückkehrt, ist der Thread noch nicht gestartet, sondern das Starten läuft gerade im Hintergrund. gleichzeitig änderst du in der Schleife den Wert, dessen Adresse du übergeben hast

    mögliche Lösung: nicht Adresse von Wert übergeben, sondern Wert



  • ahh ok stimmt.

    vielen dank für die hilfe 🙂



  • tp = malloc(sizeof(*tp))
    

    Wo ist dein free? 🙄


  • Mod

    dd++ schrieb:

    mögliche Lösung: nicht Adresse von Wert übergeben, sondern Wert

    Da hast du nicht wirklich die Wahl, da das pthread-Interface alles über void* macht. Die müssen dann eben auf für den Thread eindeutige und konstante Parameter zeigen, wie bei meinem Beispiel. Du denkst gerade an C++-Threads, bei denen man beliebige Parameter übergeben kann (die dann intern sicherlich ganz was ähnliches machen, wie hier von Hand gemacht werden muss).



  • in diesem einfachen Beispiel kann man den int Parameter in einen void* casten und in der Threadmain den void* wieder in einen int. bei komplexeren Parametern kann man wie bei dir für n Threads n Parameter anlegen, oder man kann auch vor dem Starten den Parameter mit new/malloc anlegen und in der Threadmain mit delete/free wieder freigeben



  • ok wie ichs auch anstelle,ob mit oder ohne malloc, das ergebnis ist immer das gleiche. wie kann ich dem thread nur den wert übergeben?


  • Mod

    xxmaxx schrieb:

    ok wie ichs auch anstelle,ob mit oder ohne malloc, das ergebnis ist immer das gleiche. wie kann ich dem thread nur den wert übergeben?

    Das wurde dir schon erklärt, inklusive vollständigem, funktionierendem Beispiel. Wenn es immer noch nicht funktioniert, dann wirst du wohl etwas bei der Umsetzung falsch gemacht haben. Das ist aber eine Frage deiner Umsetzung. Die Antwort, wie es geht, ist weiterhin die gleiche wie sie oben mehrfach gegeben wurde.

    (Ich würde es aber auf keinen Fall so machen, wie dd++ sagt. Das ist unsauber.)



  • sorry. ich hab nicht bemerkt dass noch geantwortet wurde. hab ich erst jetzt
    gesehen.
    ok ich geb jetzt den speicher in den threads frei. scheint zu funktionieren.
    nur versteh ich nicht ganz wieso. wenn das ganze so funktioniert wie dd++
    zuvor erklärt hat.

    in deinen Beispiel gibst du in pthread_create keinen Wert, sondern einen
     Zeiger auf einen Wert. pthread_create ist asynchron, d.h. wenn die Funktion 
    zurückkehrt, ist der Thread noch nicht gestartet, sondern das Starten läuft 
    gerade im Hintergrund. gleichzeitig änderst du in der Schleife den Wert, dessen
     Adresse du übergeben hast
    

    da würde es ja keinen unterschied machen wenn ich den speicher freigebe oder?
    wenn der thread noch nicht gestartet ist und im hintergrund läuft und ich den wert in der schleife ändere dürfte das ganze doch trotzden nicht funktionieren. da ich den speicher ja erst "später" wieder freigebe.


  • Mod

    Häh, welchen Speicher gibst du frei? Du sollst überhaupt gar nichts allokieren, geschweige denn freigeben! (knivil wollte dich darauf hinweisen, dass du, wenn du schon manuell Speicher verwaltest, es wenigstens richtig machen sollst.)

    Guck dir nochmal mein Beispiel an.

    da würde es ja keinen unterschied machen wenn ich den speicher freigebe oder?
    wenn der thread noch nicht gestartet ist und im hintergrund läuft und ich den wert in der schleife ändere dürfte das ganze doch trotzden nicht funktionieren. da ich den speicher ja erst "später" wieder freigebe.

    Wie gut kannst du C? Ich habe den Eindruck du bist gerade ins tiefe Wasser gesprungen, obwohl noch Üben im Nichtschwimmerbecken angesagt ist. Was keine Schande ist, jeder muss erst einmal lernen.
    Threads sind nicht ganz einfach. Du musst schon fit sein in Programmierung mit void*, um die Threads überhaupt nutzen zu können (im technischen Sinn) und natürlich im "parallelen" Denken, um etwas sinnvolles damit machen zu können. Das geht sicherlich beides nicht, wenn du noch Schwierigkeiten mit einfachen Ablaufplänen und er Vorstellung, was ein Objekt/Variable ist, hast.

    Die Erklärung, die ich und dd++ gegeben haben, wieso dein Programm nicht funktioniert, passt schon. Ich verstehe deinen Einwand überhaupt gar nicht. Er ist wirr.



  • wieso wirr? d++ hat doch geschrieben:

    bei komplexeren Parametern kann man wie bei dir für n Threads n Parameter anlegen, oder man kann auch vor dem Starten den Parameter mit new/malloc anlegen und in der Threadmain mit delete/free wieder freigeben
    

    gut hab ich dann wohl falsch verstanden. dein beispiel funktioniert ja. ich möchte halt nur nicht n parameter anlegen.


  • Mod

    Die Threads darfst du natürlich schon dynamisch anlegen, wenn du willst. Aber du darfst ihnen nicht den Boden unter den Füßen wegziehen, während sie noch laufen! Ein einfaches Beispiel:

    #include <pthread.h>
    #include <stdlib.h>
    #include <stdio.h>
    
    typedef struct
    {
      int arg1;
      double arg2;
      pthread_t thread;
    } thread_parameters;
    
    void* function(void* generic_args)
    {
      thread_parameters *args = generic_args;
      printf("Dies ist Thread %i mit Argument %f\n", args->arg1, args->arg2);
      return NULL;
    }
    
    int main()
    {
      unsigned num_threads;
      thread_parameters *threads;
    
      puts("Wie viele Threads?");
      scanf("%u", &num_threads);
    
      if ((threads = malloc(num_threads * sizeof *threads)))
        {
          unsigned i;
          for (i = 0; i < num_threads; ++i)
            {
              threads[i].arg1 = i;
              threads[i].arg2 = i * 1.234;
              pthread_create(&threads[i].thread, NULL, function, threads + i);
            }
            for (i = 0; i < num_threads; ++i)
            {
              pthread_join(threads[i].thread, NULL);
            }    
        }
      free (threads);
      return 0;
    }
    

    Verbesserungswürdig ist noch die Erstellung, auf das Ende Warten und Zerstörung der Threads. Das könnte man schöner wegkapseln. Aber im Prinzip ist es das. Keine Speicherlöcher, kein verschwendeter Speicher, keine Race-Condition.
    Könnte auch noch ein bisschen mehr Fehlerbehandlung vertragen (wie in Zeile 27), aber ich wollte das Beispiel nicht damit vollmüllen. Fehlerbehandlung in reinem C tendiert leider dazu, etwas unübersichtlich zu werden :p .



  • ok jz is alles klar.
    danke 🙂


Anmelden zum Antworten