pthread nutzen



  • Hallo,

    In meiner main-funktion werden Daten aus einem pointer-struct gelesen, verarbeitet und über eine GUI ausgegeben.

    Das einlesen erfolgt über eine seperate Funktion, die mit diverser Hardware kommuniziert und mir permanent aktualisierte Daten (z.B. Temperaturwerte) in mein Struct schiebt. Dieser Funktion übergebe ich den Pointer auf das Struct.
    Diese einlese-Funktion "readData" beinhaltet einen Initialiserungsteil der Hardware und enthaelt dann eine while(1) Dauerschleife.
    Demnach arbeitet diese beiden Programmteile dauerhaft und unabhaengig voneinander. Soweit ich weis löse ich das ganze mittels eines Threads.

    Ich programmiere in C und meine GUI habe ich mittels GTK+ programmiert.

    Hier noch ein Teil meines Codes:

    [code]
    typedef struct{
      int variable1;
      int variable2;
    } sarray;
    
    typedef struct _workfields{
      sarray array1[5];
      ...
    } WorkFields;
    
    void funktion1(WorkFields *work){ //Funktion wird nur ueber Thread readData genutzt
      //verarbeitung und zurück ins Struct *work schreiben
      work->array1[0].variable1 = work->array[0].variable1 * 20;
    }
    
    void readData(WorkFields *work){ //hiervon Thread erzeugen
      //Initialisierungen...
    
      while(1){
      //aus Schnittstelle Daten empfangen und ins Struct *work schreiben
      work->array1[0].variable1 = ...;
      funktion1(work);
      }
    } 
    
    void time_handler(WorkFields *work){
      //liest die eingelesene variable1 ohne sie zu veraendern
      //und veraendert selber die variable2
      ... = work->array1[0].variabel1;
      work->array1[0].variable2 = ...;
    }
    
    int main(){ 
    
     ...
     WorkFields      *work;   
     work = g_slice_new(WorkFields); 
    
     pthread_t thread1; 
     g_thread_init (NULL); 
     gdk_threads_init (); 
     gdk_threads_enter (); 
    
     //das restliche Programm, das einmalig abläuft
    
     pthread_create (&thread1, NULL, readData, work); 
    
     g_timeout_add(1000, (GSourceFunc) time_handler, work); //gehört zum GTK Prog erzeugt Dauerlauf
     gtk_widget_show_all(work->window1); 
     time_handler(work);
    
     gtk_main (); 
     gdk_threads_leave (); 
    
    return 0; 
    }
    

    Wenn ich es richtig verstanden habe, benötige ich keinen Mutex, da ich eine Variable1 aus dem Struct nur vom einen Programmteil beschreibe und vom anderen Programmteil lese und es somit zu keinem undefinierten Zustand kommen kann.

    Der Code mit den Variablen ist nur ein einfaches Beispiel, wundert euch nicht warum ich in der funktion1 den Wert*20 nehme...

    Was habe ich falsch gemacht?

    Viele Grüße 🙂



  • Was habe ich falsch gemacht?

    Nonsens Frage. Wir sind hier nicht beim Quiz.
    Was für ein Fehler tritt deiner Meinung nach wo auf?



  • JohnDillinger schrieb:

    In meiner main-funktion werden Daten aus einem pointer-struct gelesen, verarbeitet und über eine GUI ausgegeben.

    Was ist ein Pointer-Struct?

    JohnDillinger schrieb:

    Demnach arbeitet diese beiden Programmteile dauerhaft und unabhaengig voneinander. Soweit ich weis löse ich das ganze mittels eines Threads.

    Soweit du weißt?!?!

    JohnDillinger schrieb:

    Hier noch ein Teil meines Codes:

    Welcher Rotz ist:

    test.c:8:3: Fehler: expected specifier-qualifier-list before »...« token
       ...
       ^
    test.c: In Funktion »funktion1«:
    test.c:15:35: Fehler: »WorkFields« hat kein Element namens »array«
       work->array1[0].variable1 = work->array[0].variable1 * 20;
                                       ^
    test.c: In Funktion »readData«:
    test.c:23:31: Fehler: expected expression before »...« token
       work->array1[0].variable1 = ...;
                                   ^
    test.c: In Funktion »time_handler«:
    test.c:32:3: Fehler: expected expression before »...« token
       ... = work->array1[0].variabel1;
       ^
    test.c:33:31: Fehler: expected expression before »...« token
       work->array1[0].variable2 = ...;
                                   ^
    test.c: In Funktion »main«:
    test.c:39:2: Fehler: expected expression before »...« token
      ...
      ^
    test.c:41:2: Fehler: »work« nicht deklariert (erste Benutzung in dieser Funktion)
      work = g_slice_new(WorkFields); 
      ^
    test.c:41:2: Anmerkung: jeder nicht deklarierte Bezeichner wird nur einmal für jede Funktion, in der er vorkommt, gemeldet
    test.c:41:21: Fehler: expected expression before »WorkFields«
      work = g_slice_new(WorkFields); 
                         ^
    test.c:44:2: Fehler: unbekannter Typname: »pthread_t«
      pthread_t thread1; 
      ^
    test.c:45:17: Fehler: »NULL« nicht deklariert (erste Benutzung in dieser Funktion)
      g_thread_init (NULL); 
                     ^
    test.c:53:23: Fehler: »GSourceFunc« nicht deklariert (erste Benutzung in dieser Funktion)
      g_timeout_add(1000, (GSourceFunc) time_handler, work); //gehört zum GTK Prog erzeugt Dauerlauf
                           ^
    test.c:53:36: Fehler: expected »)« before »time_handler«
      g_timeout_add(1000, (GSourceFunc) time_handler, work); //gehört zum GTK Prog erzeugt Dauerlauf
    

    c.rackwitz schrieb:

    Wenn du Code postest, dann bitte nur ein kleines darstellendes Programm, welches man sofort und ohne Umwege kompilieren kann. An dieser kleinen Demo soll dein Problem klar dargestellt sein. Die Demo darf keinen Code enthalten, der nicht am Problem beteiligt ist (abgesehen von allem, was zu einem kompilierbaren Programm gehoert).

    JohnDillinger schrieb:

    Wenn ich es richtig verstanden habe, benötige ich keinen Mutext, da ich eine Variable1 aus dem Struct nur vom einen Programmteil beschreibe und vom anderen Programmteil lese und es somit zu keinem undefinierten Zustand kommen kann.

    1. Was ist ein Mutext?
    2. Bist du sicher, dass das Schreiben und Lesen des Wertes atomar ist?

    JohnDillinger schrieb:

    Der Code mit den Variablen ist nur ein einfaches Beispiel, wundert euch nicht warum ich in der funktion1 den Wert*20 nehme...

    Du meinst den Code, der nicht mal ansatzweise kompiliert? Kein Problem. Kann's ja eh nicht laufen lassen.

    JohnDillinger schrieb:

    Was habe ich falsch gemacht?

    Viel?



  • Wutz schrieb:

    Was habe ich falsch gemacht?

    Nonsens Frage. Wir sind hier nicht beim Quiz.
    Was für ein Fehler tritt deiner Meinung nach wo auf?

    jo du hast Recht entschuldigung.

    Fehler:

    note: exptected ´void * (*)(void *)´ but argument is of type ´void (*)(struct WorkFields *)´
    

    Wenn ich mir die Syntax von pthread_create anschaue, erwartet er als letztes Argument ein (void 😉 ich uebergebe ihm aber ein (struct *).

    Mag er wohl nicht wenn 2 Threads den selben Pointer bekommen. 😕
    Wie laesst sich das Problem lösen?

    Lg



  • Der Rückgabetyp deiner Thread-Funktion (z.B. readData) ist falsch. Er muss ein void* sein - nicht void.

    http://pubs.opengroup.org/onlinepubs/007908775/xsh/pthread_create.html

    // edit
    Der struct WorkFields* wird in C implizit zu void* konvertiert - sollte also in Ordnung sein.



  • Hat jemand noch eine Idee?

    Folgendes ist mir aufgefallen. Heute habe ich die IDE (-ich programmiere übrigens unter Linux mit CodeBlocks-) neu gestartet und beim erstmaligen kompilieren und ausführen scheint es zu funktionieren. Zumindest läuft er ohne Probleme durch und plottet mir Werte.

    Aendere ich jetzt irgendetwas im Programm und kompiliere es, kommt die besagte Fehlermeldung und es wird die Datei pthread.h mit Verweis auf die Syntax von pthread_create geöffnet.

    Wenn ich jetzt einfach nochmal auf kompilieren und ausführen gehe, ignoriert er es einfach, startet die Console und plottet die Werte...

    Was ist da los?

    Hier nochmal das Beispielprogramm mit dem ich das gerade getestet habe.

    Gruß

    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    #include <gtk/gtk.h>
    #include <pthread.h>
    
    typedef struct{
    int variable1;
    int variable2;
    } sarray;
    
    typedef struct _workfields{
    sarray array1[5];
    
    } WorkFields;
    
    void funktion1(WorkFields *work){ //Funktion wird nur ueber Thread readData genutzt
    //verarbeitung und zurück ins Struct *work schreiben
    work->array1[0].variable1 = work->array1[0].variable1 * 5;
    }
    
    void readData(WorkFields *work){ //hiervon Thread erzeugen
    //Initialisierungen...
    
    while(1){
    //aus Schnittstelle Daten empfangen und ins Struct *work schreiben
    work->array1[0].variable1 = 1;
    funktion1(work);
    }
    }
    
    void time_handler(WorkFields *work){
    //liest die eingelesene variable1 ohne sie zu veraendern
    //und veraendert selber die variable2
    int test;
    int test2;
    
    test = work->array1[0].variable1;
    work->array1[0].variable2 = test*10;
    test2 = work->array1[0].variable2;
    
    printf("%d\n", work->array1[0].variable2);
    }
    
    int main(int argc, char *argv[]){
    
    WorkFields *work;
    work = g_slice_new(WorkFields);
    
    pthread_t thread1;
    g_thread_init (NULL);
    gdk_threads_init ();
    gdk_threads_enter ();
    
    //das restliche Programm, das einmalig abläuft
    
    pthread_create (&thread1, NULL, readData, work);
    
    g_timeout_add(1000, (GSourceFunc) time_handler, work);  
    
    time_handler(work);
    
    gtk_main ();
    gdk_threads_leave ();
    
    return 0;
    }
    


  • JohnDillinger schrieb:

    Folgendes ist mir aufgefallen. Heute habe ich die IDE (-ich programmiere übrigens unter Linux mit CodeBlocks-) neu gestartet und beim erstmaligen kompilieren und ausführen scheint es zu funktionieren. Zumindest läuft er ohne Probleme durch und plottet mir Werte, ob die jetzt stimmen oder nicht hab ich nicht beachet.
    Aendere ich jetzt irgendetwas im Programm und kompiliere es, kommt die besagte Fehlermeldung und es wird die Datei pthread.h mit Verweis auf die Syntax von pthread_create geöffnet.

    Wenn ich jetzt einfach nochmal auf kompilieren und ausführen gehe, ignoriert er es einfach, startet die Console und plottet die Werte...

    Wahrscheinlich baut der Linker die Anwendung nicht neu, wenn ein Fehler geworfen wurde, und du startest einen erfolgreichen Build. Das wäre so die erste Vermutung, die ich hätte.

    Was dein Programm angeht - kenne mich nicht mit CodeBlocks aus, aber es hätte geholfen, wenn du da irgendwo her die Kommandozeile hättest exportieren können. Bei mir baut es mit:

    gcc test.c -o test `pkg-config --cflags gtk+-3.0` `pkg-config --libs gtk+-3.0` -lpthread -lgthread-2.0
    

    Gibt aber auch einen Haufen Warnungen raus:

    test.c: In Funktion »main«:
    test.c:60:1: Warnung: »g_thread_init« is deprecated (declared at /usr/include/glib-2.0/glib/deprecated/gthread.h:265) [-Wdeprecated-declarations]
     g_thread_init (NULL);
     ^
    test.c:61:1: Warnung: »gdk_threads_init« is deprecated (declared at /usr/include/gtk-3.0/gdk/gdkthreads.h:44) [-Wdeprecated-declarations]
     gdk_threads_init ();
     ^
    test.c:62:1: Warnung: »gdk_threads_enter« is deprecated (declared at /usr/include/gtk-3.0/gdk/gdkthreads.h:46) [-Wdeprecated-declarations]
     gdk_threads_enter ();
     ^
    test.c:66:33: Warnung: Übergabe des Arguments 3 von »pthread_create« von inkompatiblem Zeigertyp
     pthread_create (&thread1, NULL, readData, work);
                                     ^
    In file included from /usr/include/glib-2.0/glib/deprecated/gthread.h:128:0,
                     from /usr/include/glib-2.0/glib.h:107,
                     from /usr/include/gtk-3.0/gdk/gdkconfig.h:13,
                     from /usr/include/gtk-3.0/gdk/gdk.h:30,
                     from /usr/include/gtk-3.0/gtk/gtk.h:30,
                     from test.c:4:
    /usr/include/pthread.h:235:12: Anmerkung: »void * (*)(void *)« erwartet, aber Argument hat Typ »void (*)(struct WorkFields *)«
     extern int pthread_create (pthread_t *__restrict __newthread,
                ^
    test.c:74:1: Warnung: »gdk_threads_leave« is deprecated (declared at /usr/include/gtk-3.0/gdk/gdkthreads.h:48) [-Wdeprecated-declarations]
     gdk_threads_leave ();
    

    Abgesehen von den ganzen Warnungen, die du bekommst, weil du deprecated functions verwendest ...
    void time_handler(WorkFields *work) sollte eigentlich void*time_handler(void*param) sein, und in der Funktion erstellst du dann einen ordentlichen Zeiger auf das Objekt. Sonst kommt es halt zur Warnung - das, und natürlich die Tatsache, dass ein void* als Rückgabetyp verlangt wird, aber das hat ja schon theta gesagt, und du hast es erfolgreich ignoriert.

    void*time_handler(void*param)
    {
            WorkFields*work = (WorkFields*)param;
            ...
    
            return NULL;
    }
    

    Das schönste: wenn'de ufffer Kommandozeile gebaut hättest, hättest du das auch rausfinden können.

    Und bitte, gewöhne dir einen anderen Stil an. Dein Code ist so vollkommen unlesbar, das geht gar nicht:

    void*readData(void*param)
    {
            WorkFields *work = (WorkFields*)param;
    
            while(1)
            {
                    work->array1[0].variable1 = 1;
                    funktion1(work);
            }
            return NULL;
    }
    

    Ist so ungefähr 9000 mal lesbarer.

    @theta: Ich weiß nicht - bei mir haut er die Warnung auch raus, wenn ich den Originaltyp drinlasse in der Parameterliste..Ich weiß - die Zeiger sind gleich größ, da sollte es zu keinem Problem kommen - aber die Warnung wird trotzdem generiert. Ich kann auch ein bisschen verstehen, warum - better safe than sorry halt.

    EDIT: stattdessen kannst du auch:

    pthread_create (&thread1, NULL, (void*(*)(void*))readData, work);
    

    Funktionszeigerkonvertierungen - huehue!
    Und readData ist dann wieder

    void*readData(WorkFields*work)
    

    Dann hält's der Compiler das Maul, und es crashed auch nicht.



  • Das mit den Warnungen liegt bei dir daran, das du GTK3.0 und ich GTK2.0 benutze.

    Ich bekomme lediglich die Warnungen das Variable test2 unused ist (klar),

    und "passing argument 3 of 'pthread_create' from incompatile pointer type (enabled by default)"

    sowie das g_threads_init deprecated ist. Ok das habe ich auch hier gefunden:
    https://developer.gnome.org/glib/stable/glib-Deprecated-Thread-APIs.html#g-thread-init Wenn ich es auskommentiere "funktioniert" es immernoch.

    Ich hatte aber noch keinen erfolgreichen Build mit pthread, wie soll er dann diesen starten?

    Hast Recht, die Einrückungen fehlten, da ich sie vom Linux Rechner in ein File kopiert und dann von woanders hochgeladen hatte. (Formatierungen weg)

    EDIT: Sehr geil funktioniert echt 🙂 👍



  • JohnDillinger schrieb:

    Das mit den Warnungen liegt bei dir daran, das du GTK3.0 und ich GTK2.0 benutze.

    Hm, gut, mit GTK2.0 kommen die Meldungen nicht. Darum ging's ja primär auch nicht.

    JohnDillinger schrieb:

    Ich hatte aber noch keinen erfolgreichen Build mit pthread, wie soll er dann diesen starten?

    Wie gesagt ... ich weiß nicht, wie CodeBlocks arbeitet.
    Aber wenn ich's vermuten müsste, würde ich sagen, dass das Objektfile zwar generiert, aber nicht gelinkt wird, weil halt Fehler rauskamen. Aber für das Projekt ist der Source beim Neustart nicht dirty,, deswegen wird der Linker direkt mit dem Objektfile aufgerufen, der stellt keine Probleme beim Linken fest, generiert dir dein Binary, und das läuft ja auch. Hat theta auch schon gesagt - void* und jeder andere Zeiger sind von der Größe her praktisch gleich (außer bei der segmentierten Speicherverwaltung, ja, ja, die Korinthenkacker lauern hier auch an jeder Ecke).

    Ist aber nur eine Vermutung. Nachgeprüft habe ich es nicht.

    JohnDillinger schrieb:

    EDIT: Sehr geil funktioniert echt 🙂 👍

    Pass aber auf, dass der Rückgabewert der Funktion void* ist. Nur für den Fall, dass du irgendwann man Fehlerbehandlung machen musst.



  • dachschaden schrieb:

    JohnDillinger schrieb:

    Das mit den Warnungen liegt bei dir daran, das du GTK3.0 und ich GTK2.0 benutze.

    Hm, gut, mit GTK2.0 kommen die Meldungen nicht. Darum ging's ja primär auch nicht.

    JohnDillinger schrieb:

    Ich hatte aber noch keinen erfolgreichen Build mit pthread, wie soll er dann diesen starten?

    Wie gesagt ... ich weiß nicht, wie CodeBlocks arbeitet.
    Aber wenn ich's vermuten müsste, würde ich sagen, dass das Objektfile zwar generiert, aber nicht gelinkt wird, weil halt Fehler rauskamen. Aber für das Projekt ist der Source beim Neustart nicht dirty,, deswegen wird der Linker direkt mit dem Objektfile aufgerufen, der stellt keine Probleme beim Linken fest, generiert dir dein Binary, und das läuft ja auch. Hat theta auch schon gesagt - void* und jeder andere Zeiger sind von der Größe her praktisch gleich (außer bei der segmentierten Speicherverwaltung, ja, ja, die Korinthenkacker lauern hier auch an jeder Ecke).

    Ist aber nur eine Vermutung. Nachgeprüft habe ich es nicht.

    JohnDillinger schrieb:

    EDIT: Sehr geil funktioniert echt 🙂 👍

    Pass aber auf, dass der Rückgabewert der Funktion void* ist. Nur für den Fall, dass du irgendwann man Fehlerbehandlung machen musst.

    jo danke.


Log in to reply