Member als thread laufen lassen - wie?



  • 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 ...


Anmelden zum Antworten