Member als thread laufen lassen - wie?



  • Linux hat pthread um einen neuen Thread aufzusetzen.
    Jetzt kann ich damit aber nicht einen Member oder irgendwelche anderen Funktionen aufsetzen.
    Gibt's da eine andere Linuxfunktion oder Möglichkeit, oder muß ich da jetzt pthread umschreiben und Linux neu kompilieren?
    - Das wird dann unötigerweise etwas aufwendig und nicht allen Systemen zumutbar und pthread ist nicht mehr POSIX.

    Ich programmiere mit QT-3.5.8 und das hat schon QThread, jedoch mit einem
    Multicorerechner+Cache (IA) funktioniert das nicht richtig.
    Möglicherweise versucht QT auch die Variablen die benutzt werden unsinnigerweise zu locken oder zu synchronisieren.(QThread arbeitet local)

    Habe jetzt vorerst mal eine globale struct (struct scheduler_t {}scheduler_args ), die alle variablen enthält, die mein Thread verändert und auf die er reagiren muss.
    Jetzt bekomme ich 2 Linkfehler:
    /usr/qt/include/qglist.h:150: multiple definition of scheduler_args' /usr/qt/include/qshared.h:50: multiple definition ofscheduler_args'

    Irre nicht? - egal wie ich scheduler_t oder scheduler_args auch benenne!



  • bastl schrieb:

    Irre nicht?

    Ne, wahrscheinlich nicht 🙂

    // Header-Datei
    struct scheduler_t
    {
        ...
    };
    extern scheduler_t scheduler_args;
    
    // Implementation-Datei
    scheduler_t scheduler_args;
    

    bastl schrieb:

    Jetzt kann ich damit aber nicht einen Member oder irgendwelche anderen Funktionen aufsetzen.

    Was zum meinst du damit 😕



  • extern ? - jetzt bekomme ich aber richtig Probleme!
    Aber die Idee war gut:

    static scheduler_t scheduler_args;
    

    und alles funzt - danke!!!

    Jetzt kann ich damit aber nicht einen Member oder irgendwelche anderen Funktionen aufsetzen.
    Was zum meinst du damit 😕

    aufsetzen? -damit drücke ich aus, daß der Thread nicht im Programm läuft sonder nebenher - aufgesetzt, eigenständig.
    Member ? -Funktion einer Klasse in Linux POSIX Threads eintragen geht nicht, nur global deklarierte Funktionen gehen und zwar nur dieser Typ:

    viod *mythread(void *);
    

    oder kann man eine Klassenfuntion auf so einen Typ global umquetschen?
    Zu Threads sind eigentlich alle Funktionen/Member geeignet, deren Adresse zur Laufzeit existiert und bekannt ist.



  • bastl schrieb:

    extern ? - jetzt bekomme ich aber richtig Probleme!
    Aber die Idee war gut:

    static scheduler_t scheduler_args;
    

    und alles funzt - danke!!!

    Oh oh, static solltest du lieber nicht nehmen.. Mit static sagst du, dass diese Variable nur im aktuell kompilierenden Modul sichtbar/vorhanden sein soll. Das willst du aber nicht, du wolltest doch eine globale Variable, wenn ich dich richtig verstanden hab? Was funktionierte denn bei extern nicht?

    bastl schrieb:

    oder kann man eine Klassenfuntion auf so einen Typ global umquetschen?

    Irgendwie so:

    class XYZ
    {
        void aaa()
        {
            // Thread erstellen mit Funktion dummyfunc und als User-Parameter this mitgeben
        }
    
        static void* dummyfunc( void* me )
        {
            ((XYZ*)me)->threadfunc();
        }
    
        void threadfunc()
        {
            ...
        }
    };
    


  • das problem ist, dass eine methode einer klasse automatisch this als parameter hat. im allgemeinen wird der simpel als erster parameter übergeben. das muss aber nicht so sein. selbst wenn du das zeug casten kannst, ist das keine saubere lösung.

    eine lösung dafür ist, dass du eine statische methode deiner klasse hinzufügst, die du pthread_create zusammen mit einem pointer auf eine instanz deiner klasse übergibst. diese funktion nimmt den pointer und ruft die nicht-statische methode auf.

    bastl schrieb:

    Multicorerechner+Cache (IA) funktioniert das nicht richtig.

    das ist mir unklar. ich denke, hier irrst du.



  • Badestrand schrieb:

    Oh oh, static solltest du lieber nicht nehmen.. Mit static sagst du, dass diese Variable nur im aktuell kompilierenden Modul sichtbar/vorhanden sein soll. Das willst du aber nicht, du wolltest doch eine globale Variable, wenn ich dich richtig verstanden hab? Was funktionierte denn bei extern nicht?

    static sagt aus, daß du die Deklaration für den Linker sichtbar machen willst.
    Das braucht du dann, wenn du eine globale Nutzung brauchst - wie in meinem Fall.
    Mit extern gehe ich einen Schritt zu weit - für einen neuen (Kind-, forc()- ) Process brauchst du das dann, aber nicht für POSIX Thread. Hier wird nur eine Funktion in den Linux Scheduler eingetragen und bekommt somit Rechnerzeit zugeteilt und läuft damit unabhängig - bleibt aber eine Funktion in ihrer Programm Umgebung, ob du diese Funktion so aufrufst oder als thread laufen lässt, spielt für die Programm Variablen keine Rolle, die bleiben die gleichen. Und ich finde bei C++: je lokaler desto besser.

    besserwisser schrieb:

    das problem ist, dass eine methode einer klasse automatisch this als parameter hat. im allgemeinen wird der simpel als erster parameter übergeben. das muss aber nicht so sein. selbst wenn du das zeug casten kannst, ist das keine saubere lösung.

    Genau, so sehe ich das auch.
    Und casten habe ich probiert - geht nicht! - du braucht einen globalen Typ und dann kann ich gleich die Funktion global deklarieren.

    besserwisser schrieb:

    eine lösung dafür ist, dass du eine statische methode deiner klasse hinzufügst, die du pthread_create zusammen mit einem pointer auf eine instanz deiner klasse übergibst. diese funktion nimmt den pointer und ruft die nicht-statische methode auf.

    Das war glaube ich das erste, das ich propiert habe: und wie

    Badestrand schrieb:

    Oh oh, static solltest du lieber nicht nehmen.. Mit static sagst du, dass diese Variable nur im aktuell kompilierenden Modul sichtbar/vorhanden sein soll

    hast du die Abhängigkeit zu deiner Klasse nicht verloren ( *MyClass::mythread(void*) ).
    Oder poste kurz ein Beispiel, wenn es vom Badestrand seinem abweicht dann teste ichs geschwind.

    besserwisse schrieb:

    das ist mir unklar. ich denke, hier irrst du.

    Ja, habe ich mir auch nochmal überlegt und an MultiCore kanns wirklich nicht liegen.

    So mein Thread läuft jetzt wunderbar nebenher, neben sich, an sich vorbei - dolle sache! 🕶



  • Hier ein Tutorial fuer Threads in Klassen. So aehnlich mache ich das immer, k.A. ob es zum Thema passt: http://www.linuxselfhelp.com/HOWTO/C++Programming-HOWTO-18.html



  • bastl schrieb:

    static sagt aus, daß du die Deklaration für den Linker sichtbar machen willst.
    Das braucht du dann, wenn du eine globale Nutzung brauchst - wie in meinem Fall.
    Mit extern gehe ich einen Schritt zu weit - für einen neuen (Kind-, forc()- ) Process brauchst du das dann, aber nicht für POSIX Thread.

    Probier das mal:

    // Global.hpp
    #ifndef GLOBAL_INCLUDED
    #define GLOBAL_INCLUDED
    
    struct Bla
    {
    	int i;
    };
    static Bla bla_static;
    extern Bla bla_extern;
    
    #endif
    
    // Global.cpp
    #include "Global.hpp"
    
    Bla bla_extern;
    
    // Foo.cpp
    #include <iostream>
    #include "Global.hpp"
    
    void foo()
    {
        std::cout << bla_static.i << std::endl;
        std::cout << bla_extern.i << std::endl;
    }
    
    // Main.cpp
    #include <iostream>
    #include "tmp.h"
    
    void foo();
    
    int main ()
    {
    	bla_static.i = 1;
    	bla_extern.i = 2;
    	std::cout << bla_static.i << std::endl;
    	std::cout << bla_extern.i << std::endl;
    	foo();
    


  • knivil schrieb:

    Hier ein Tutorial fuer Threads in Klassen. So aehnlich mache ich das immer, k.A. ob es zum Thema passt:

    Ja, IBM hat gute Docs.
    Wenn man nach "pthread examples" googlet bekommt man auch sehr hilfreiche Beispiele und erfährt viel über den Umgang mit pthread.h und seinen Funktionen.

    Badestrand schrieb:

    Probier das mal:

    static - interne Bindung (inerhalb Programmdatei.o), Speicherbelegung fest im Hauptprogramm
    const - s. static
    inline - s. static, jedoch nur für Funktionen
    extern - externe Bindung (alle Programmdateien.o)

    Wenn man mit anderen Projekten programmiert z.B. QT kann mit extern sehr schnell was passieren (auf anderen Systemen mit anderen Versionen), das man so nicht vorgesehen hatte. Deshalb verwende ich extern nur wenn es wirklich sein muss.

    Ich glaube ich fasse nochmal kurz zusammen:
    Ich will (wenn Möglich)eine Klassenfunktion des Typs

    void *myclass::mythread( void *unused )
       {
       ...
       }
    

    Typlos machen d.h. nach ...
    (vieleicht braucht man das auch nicht und mein Ansatz ist komplet falsch)

    void *mythread( void *unused )
       {
       ...
       }
    

    konvertieren. um sie mit ...

    pthread_create( &thread, NULL, mythread, NULL )
    

    als Thread einzutragen und auszuführen.
    Verliere ich damit den Zugriff auf die lokalen Variablen ist das ganze Vorhaben sinnlos.
    Da sich viele Spezies jedoch zusammengesetzt haben und den 'POSIX Thread Standart' enwickelt haben und diese Formatierung wählten müsste das mit den Klassenfunctionen irgendwie möglich sein.
    Da sind ja schlaue Köpfe zusammen gewesen und Threads sollte man ja so offt wie möglich benutzen, d.h. wenn ich jeden Thread global deklarieren muss kann es sehr schnell eng in einer Datei.o wereden.
    Die Fehlermeldung mit einer Klassenfunktion ist in meinem Fall:

    simulationview.cpp:169: error: argument of type 'void* (SimulationView::)(void*)' does not match 'void* (*)(void*)'
    

    Es gibt ja sehr viel im Web über dieses Problem zu lesen.

    Ups??? - was hab ich gemacht? - plötzlich geht's !!! - mit static - vorhin hatte ich da aber eine Fehlermeldung ?! -hab ich wohl noch irgenwo Müll stehen lassen!

    myclass.h

    #include <pthread.h>
    
    class Myclass
       {
       public
       ...
       pthread_t thread;
       ...
       void start_thread();
       static void *mythread( void *unused );
       ...
       }
    

    myclass.cpp

    #include "myclass.h"
    
    void Myclass::start_thread()
       {
       ...
       if ( pthread_create( &thread, NULL, mythread, NULL ) )
          ...
       }
    
    void * Myclass::mythread(void * unused)
    	{
    	... //thread process
    	}
    

    Also Leute, dies ist die Lösung für die Verwendung von Klassenfunktionen, Member, mit pthread, POSIX thread.
    Musste ja ganz einfach sein - 😉 👍

    Jedoch müssen die Variablen auf die der Threadprozess zugreifen möchte global static deklariert oder der Zeiger auf diese übergeben werden!
    Also zu früh gefreut - die localen Variablen bleiben unerreichbar!



  • bastl schrieb:

    Jedoch müssen die Variablen auf die der Threadprozess zugreifen möchte global static deklariert oder der Zeiger auf diese übergeben werden!
    Also zu früh gefreut - die localen Variablen bleiben unerreichbar!

    Nein! In meinem zweiten Post in diesem Thread ist der Code, wie du auf die Klassenvariablen zugreifen kannst, besserwisser hat's dann auch noch mal erklärt, dazu noch nen Link von knivil. (🙄)

    bastl schrieb:

    Deshalb verwende ich extern nur wenn es wirklich sein muss.

    Wenn du Variablen hast, die du in mehr als einer .cpp-Datei verwenden willst, brauchst du extern!



  • Badestrand schrieb:

    Nein! In meinem zweiten Post in diesem Thread ist der Code, wie du auf die Klassenvariablen zugreifen kannst

    Ja das geht schon, stellt mich aber nicht sehr zufrieden, dann kann ich auch eine struct mit all den Variablen die threadfunc benützt global static deklarieren - macht mich auch nicht sehr glücklich.
    Das muss so einfach gehen wie das threaden einer Klassenfunktion.
    Ich lese mal weiter, da muss es etwas geben, daß meiner threadfunc den void Zeiger (4. Argument) den richtigen Klassentyp zuweist und ich somit dann diesen Zeiger in meiner threadfunc auf die lokalen Variablen und Funktionen benützen kann.
    Zu void* geht alles, bloß zurück nicht! - Warscheinlich ist das mit der dummyfunc doch eine gute Lösung! -Halt, wenn das mit dem Aufruf von threadfunc aus der dummyfunc funktioniert, dann habe ich auch zugriff auf die localen Variablen. Gleich mal testen ...

    Jo, supper, spitze, es geht doch auch ohne dummy: (hab da vorhin die 2. Klammer um used vergessen und mich geärgert, daß es nicht geht, myclass.cpp:13)

    myclass.h

    #include <pthread.h>
    
    class Myclass
       {
       public
       ...
       pthread_t thread;
       int index;
       ...
       void start_thread();
       static void *mythread( void *used );
       ...
       }
    

    myclass.cpp

    #include "myclass.h"
    
    void Myclass::start_thread()
       {
       ...
       if ( pthread_create( &thread, NULL, mythread, this ) )
          ...
       }
    
    void * Myclass::mythread(void * used)
       {
       ... //thread process
       ((Myclass*)used)->index ++;   //Zugriff auf eine locale Klassenvariable 
       }
    

    Wenn man natürlich auf viele locale Klassenvariablen zugreifen muss ist das mit der dummy dann doch besser - weniger Schreibarbeit !



  • Oder dann geht auch - finde ich ganz elegant:

    myclass.cpp

    #include "myclass.h"
    
    void Myclass::start_thread()
       {
       ...
       if ( pthread_create( &thread, NULL, mythread, this ) )
          ...
       }
    
    void *Myclass::mythread(void *used)
       {
       Myclass *p = ((Myclass*)used);   //
       ...
       p->index ++;   //Zugriff auf eine locale Klassenvariable 
       }
    


  • Im IBM doc steht drin, wie man non-static Member in Threads startet. Also wie z.B. in Java, wo man von der abstracten Klasse Thread ableitet und nur die Run-Methode (oder hier Execute) ueberschreiben braucht. Das mit dem static finde ich geschummelt.



  • knivil schrieb:

    Das mit dem static finde ich geschummelt.

    Nun ja, das minimale für einen Linkvorgang ist halt mal static; extern geht sowiso. Alles andere (locale) kann der linker nicht benützen, da es nicht mehr lesbar ist. So ist das halt in C++.
    Du musst halt die Adresse die du benützen willst in C++ irgenwie static machen - Hey dann kann man ja auch einen static Pointer deklarieren und dem die Addresse der localen threadfunc zuweisen und meine threadfunc bleibt local
    Gleich mal testen...
    Leider nein, alle Funktionen auf die Bezug genommen wird (deren Adressen explizit verwendet werden) müssen static sein.

    Naja, das sind die Sachen, die mir an C++ auch nicht gefallen. 😞



  • Leider nein, alle Funktionen auf die Bezug genommen wird (deren Adressen explizit verwendet werden) müssen static sein.

    Nein, in meinem folgenden Beispiel ist run() nicht statisch. Das, was du mit p->index++ machts, kannst du auch mit Methoden machen.

    Also, hier eine simple Threadklasse a la Java: Man kann von Thread ableiten und run ueberschreiben. Mittels start() startet man einen neuen Thread. Unterschiedliche Objekte haben dabei dann unterschiedliche Threads. Dabei muss die entry-Methode nicht innerhalb der Klasse deklariert sein, man kann sie auch ganz in der cpp-Datei verbergen.

    // simple thread class
    class Thread
    {
    public:
      Thread();
      virtual ~Thread();
    
      // Startet die Methode run() in einem eigenen Thread
      int start();
    
      // Threadroutine, wird von start()/entry() in einem
      // eigenen Thread gestartet
      virtual void run() = 0;
    
      // true wenn run gerade in einem eigenen thread ausgefuerhrt weird
      bool running();
    
      // Wartet auf die Beendigung des Threads
      int join();
      // Freigeben der Ressourcen, siehe pthread_detach
      int detach();
    private:
      // Eintrittspunkt fuer das Starten des Threads
      // nimmt this als Argument, und fuert dann
      // run aus, wird von start() benutzt
      static void* entry(void* me);
    
      Thread(const Thread&);
      pthread_t threadId;
      bool state;
    };
    
    void* Thread::entry(void* me)
    {
      //Thread* pthis = dynamic_cast<Thread*>(me);
      Thread* pthis = (Thread*)(me);
    
      // soll immer und ueberall gecancelt werden koennen
      pthread_setcancelstate(PTHREAD_CANCEL_ENABLE,0);
      pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS,0);
      //pthis->state = true;
      pthis->run();
      pthis->state = false;
      return 0;
    }
    
    int Thread::start()
    {
      return pthread_create(&threadId, 0, Thread::entry, (void*)this);
    }
    
    int Thread::join()
    {
      if ((threadId != 0)) // && (state))
      {
        return pthread_join(threadId, 0);
      }
      return -1;
    }
    


  • bastl schrieb:

    Nun ja, das minimale für einen Linkvorgang ist halt mal static; extern geht sowiso. Alles andere (locale) kann der linker nicht benützen, da es nicht mehr lesbar ist. So ist das halt in C++.

    Nichts für ungut, aber ich denke, du hast das Linkermodell von C++ kein Stück verstanden. Ob eine Methode einer Klasse static ist oder nicht, hat mit dem Linker komplett rein gar nichts zu tun. Und was soll der Linker nicht benutzer können, weil es "nicht mehr lesbar ist"?

    Von einer Threadklasse zu erben finde ich Design-technisch zweifelhaft. Die meisten Objekte benutzen doch eher Threads und sind nicht etwa welche. Und wenn ich auf einem "Hase"-Objekt eine "running()"-Methode ausführe, erwarte ich auch eher anderen Informationsgehalt...
    edit: Will sagen: Sicherlich Geschmackssache, ich präferiere aber ganz klar Membervariablen für's Halten des/der Threads.



  • Badestrand schrieb:

    Von einer Threadklasse zu erben finde ich Design-technisch zweifelhaft. Die meisten Objekte benutzen doch eher Threads und sind nicht etwa welche. Und wenn ich auf einem "Hase"-Objekt eine "running()"-Methode ausführe, erwarte ich auch eher anderen Informationsgehalt...
    edit: Will sagen: Sicherlich Geschmackssache, ich präferiere aber ganz klar Membervariablen für's Halten des/der Threads.

    Das haengt natuerlich von deinem Design ab und ob es sinnvoll ist, ob Hasen Threads sind bzw. benutzen. Wenn run() aufgerufen wird, dann startet ja auch kein neuer Thread. Ich habe es schon verwendet und der Code wurde einfach und uebersichtlich. Auch praktisch, wenn man Objekte als Closures betrachtet und nutzen moechte.



  • Badestrand schrieb:

    Nichts für ungut, aber ich denke, du hast das Linkermodell von C++ kein Stück verstanden. Ob eine Methode einer Klasse static ist oder nicht, hat mit dem Linker komplett rein gar nichts zu tun. Und was soll der Linker nicht benutzer können, weil es "nicht mehr lesbar ist"?

    Ja.
    Und wenn ich die Objektdatei durchschaue, dann ist ausführbarer Kode, also auch jede Funktion static - steht also neben "main" im CS-Descriptor, wenn man das so sagen darf. Macht also praktisch (für den Linker) keinen unterschied ob man eine Funktion static deklariert oder nicht?

    Badestrand schrieb:

    ich präferiere aber ganz klar Membervariablen für's Halten des/der Threads.

    Genau, Wenn man sich mal überlegt, dann gibt es nur 2 Möglichkeiten einen Thread zu stoppen:
    1.) Linux sagen, daß es den Thread mit der Nummer killen soll.
    2.) Der Thread überwacht eine Statusvariable und wenn diese den Wert z.B. STOP aufweist, beendet sich der Thread von alleine.

    Und die 2. finde ich auch die elegantere, die braucht man ja auch zum synkronisieren.
    Z.B. bekommt ein Thread mehrere Aufgaben, die er aber nur ausführen darf, wenn die Speicherumgebung dies auch erlaubt:

    void *mythread( void *Umgebung ) //Umgebung ist die Klasse Myclass
       {
       Myclass *p = ((Myclass*)Umgebung);
    
       while( ! p->STOP )
          {
          if( p->OK )
             switch( p->aufgabe )
                {
                case DO_X_MAL_Y:
                   {
                   p->ergebnis = p->X * p->Y;   //Wenn !OK ist das ergebnis nutzbar!
                   p->anzahl ++;
                   if( p->anzahl >= p->genug )   //X*Y wird genug-anzahl mal ausgeführt 
                      p->OK = FALSE;
                   }
                case FENSTERPUTZEN:
                ...
                }
          }
       }
    

    Somit kann man den Thread steuern und synkronisieren.



  • Genau, Wenn man sich mal überlegt, dann gibt es nur 2 Möglichkeiten einen Thread zu stoppen:
    1.) Linux sagen, daß es den Thread mit der Nummer killen soll.
    2.) Der Thread überwacht eine Statusvariable und wenn diese den Wert z.B. STOP aufweist, beendet sich der Thread von alleine.

    Es gibt viele Moeglichkeiten. Punkt 2) geht auch, mit der Threadklasse oben, implementiere eine stop()-Methode und die while()-Schleife in run() wird beeendet. Haengt natuerlich von der Implementation der run()-Methode ab. Zumal mit einem Objekt, die Aufgaben des Threads besser gekapselt werden koennen, da run() auf private Member der Klasse zugreifen kann, die von aussen nicht zugaenglich sind. Desweiteren ist auch die Erzeugung (Vernichtung) von Threads besser gekapselt. Soll nicht mit pthread sondern mit WinCreateThread (k.A. wie genau das heisst) ein Thread erzeugt werden, dann braucht nur die Implementation von start(), detach(), kill(), join() einer einzigen Klasse angepasst werden, am Interface sollte sich nichts aendern. Das finde ich sehr schoen und sauber.

    Btw.: Es heisst synchronisieren ...


Log in to reply