Thread und Memory



  • Hallo zusammen

    ich bin noch relativ neu in C++ und hab folgendes Problem. In meiner Anwendung hab ich ein Memoryleak. Dieses besteht weil Speicher der für Threads verwendet wird, nicht mehr freigegeben wird.

    int main () {
       bool doTerminate = false;
    
        while (!doTerminate) {
        ...
    
        for (int i = 0; i < 5; i++)
            macheWas(i);
    
        ...
        }
    
        return(0);
    }
    
    void function macheWas(int no) {
        MeinThread *t = new MeinThread();
    
        t->setProperty(no);
    
        t->run();
    }
    

    Den Speicher den ich für die Variable t benötige, wird natürlich so nie mehr frei gegeben. Welches ist die eleganteste Lösung zu diesem Problem? Muss ich eine Liste führen mit allen Threads die ich im Verlauf des Programms erzeuge und diese dann zyklisch prüfen? Wenn sie nicht mehr laufen sollten, dann den Speicher mit delete freigeben?

    Bin froh um jeden Hinweis. Besten Dank schon mal
    trple-m



  • man könnte z.b. alle threads in einem std::vector speichern und denen am schluss des programms ein signal schicken, dass die sich beenden sollen. danach könnteste den status der threads abfragen und wenn die nicht mehr laufen die c++ objekte deleten. oder du machst 'ne 'threadgroup' klasse die die threads managed.

    btw: threads und oop passen irgendwie nicht richtig zusammen...



  • delete this könnte dein Freund sein.



  • Es gibt attached threads und detached threads. Ich habe dafür 2 Klassen. Klassen, die einen attached Thread repräsentieren, lege ich auf dem Stack an. Der Destruktor führt einen pthread_join durch, so daß das Thread-Objekt frei gegeben wird, wenn der Thread beendet wird. Detached Threads werden nicht mit pthread_join gejoint, sondern beenden sich einfach. Da kann das Thread-Objekt am Ende vom pthread_start einfach das Objekt löschen.

    Das sieht dann im Falle eines attached Threads etwa so aus:

    #include <cxxtools/thread.h>
    class MyThread : cxxtools::AttachedThread
    {
      protected:
        void run();  // die ist virtuell in cxxtools::AttachedThread
    };
    void MyThread::run()
    {
      // tue was
    }
    int main()
    {
      MyThread thread1;
      MyThread thread2;
      thread1.create();
      thread2.create();
      // hier laufen thread1 und thread2 parallel
    } // main wird beendet und gibt den Speicher der Objekte frei, sobald
      // beide Threads fertig sind
    

    Und jetzt detached Threads:

    #include <cxxtools/thread.h>
    class MyThread : cxxtools::DetachedThread
    {
      protected:
        void run();  // die ist virtuell in cxxtools::DetachedThread
    };
    void MyThread::run()
    {
      // tue was
    }
    int main()
    {
      MyThread* thread1 = new MyThread(); // detached Threads werden auf dem Heap angelegt
      MyThread* thread2 = new MyThread();
      thread1->create();
      thread2->create();
      // Hier laufen thread1 und thread2 parallel
      // Wenn sie fertig sind, wird der Speicher frei gegeben
      // Daher darf nach dem create nicht mehr auf die Pointer zugegriffen
      // werden, da die Objekte bereits gelöscht sein könnten.
    
      sleep(1000);
    
    } // Hier werden beide Threads einfach hart beendet, wenn sie noch nicht am
      // Ende sind. In dem Fall wird der Speicher nicht explizit frei gegeben.
    


  • Warum nicht so:

    (new MyThread())->create();
    

    dann kann man danach nicht mehr versehentlich auf die Instanzen zugreifen.



  • Daran hatte ich auch gedacht, aber nicht gemacht. Danke für die Ergänzung (ich wollte ja nur mal sehen, ob jemand mitdenkt 🤡 ).



  • net schrieb:

    man könnte z.b. alle threads in einem std::vector speichern und denen am schluss des programms ein signal schicken, ...

    geht leider nicht, da dieses Programm normalerweise 24/7 läuft...

    net schrieb:

    btw: threads und oop passen irgendwie nicht richtig zusammen...

    warum sollen threads und oop nicht zusammen passen...?

    Ben04 schrieb:

    delete this könnte dein Freund sein.

    ich hab delete this in die Funktion MyThread::run() am Ende eingefügt, doch leider ohne Erfolg. Die Threads werden nicht mal mehr beendet...

    BenQ schrieb:

    Warum nicht so:
    (new MyThread())->create();

    geht leider auch nicht, da der Speicher auch nicht freigegeben wird...

    tntnet schrieb:

    Es gibt attached threads und detached threads.

    Detached Threads scheinen das richtige für mich zu sein. Hast du noch ein bisschen mehr Code dazu, damit ich das ganze verstehe?

    gruss
    trple-m



  • trple-m schrieb:

    Ben04 schrieb:

    delete this könnte dein Freund sein.

    ich hab delete this in die Funktion MyThread::run() am Ende eingefügt, doch leider ohne Erfolg. Die Threads werden nicht mal mehr beendet...

    Naja, wenn sich der Thread nie beendet ist auch klar warum er nie bei delete this ankommt, oder?!

    Es seidenn, im Destruktor der Threadklasse befindet sich noch Code der auf das Ende des Threads wartet, dann kann das nicht gehen weil ein Thread nicht auf sein eigenes Ende warten kann 😉



  • trple-m schrieb:

    BenQ schrieb:

    Warum nicht so:
    (new MyThread())->create();

    geht leider auch nicht, da der Speicher auch nicht freigegeben wird...

    doch - die Thread-Klasse cxxtools::DetachedThread gibt das Objekt nach Beendigung des Threads frei.

    trple-m schrieb:

    tntnet schrieb:

    Es gibt attached threads und detached threads.

    Detached Threads scheinen das richtige für mich zu sein. Hast du noch ein bisschen mehr Code dazu, damit ich das ganze verstehe?

    gruss
    trple-m

    Hier ist ein Auszug aus der Implementierung der Klasse cxxtools::DetachedThread:

    void* DetachedThread::start(void* arg)
    {
      DetachedThread* t = static_cast<DetachedThread*>(arg);
      try
      {
        t->run();
      }
      catch (...)
      {
      }
      delete t;
      return 0;
    }
    
    void DetachedThread::create()
    {
      int ret = pthread_create(&pthreadId, &pthread_attr, start, (void*)this);
      if (ret != 0)
        throw ThreadException("pthread_create", ret);
    }
    

    In der create-Methode wird also über pthread_create ein Thread gestartet. Die Startroutine DetachedThread::start führt die Thread-Methode run() aus und löscht nach Beendigung des Threads das Objekt.

    Du findest cxxtools auf meiner Homepage http://www.tntnet.org/.

    Gruß

    Tntnet



  • LordJaxom schrieb:

    Naja, wenn sich der Thread nie beendet ist auch klar warum er nie bei delete this ankommt, oder?!

    nur wenn ich delete this in der Funktion run() stehen habe, beendet er sich nicht. sonst beendet sich der Thread, aber der Speicher wird nicht freigegeben. Der Destruktor wird auch nicht aufgerufen. Soweit ich weiss, wird der Destruktor erst aufgerufen, wenn ich die Variable lösche... oder verstehe ich da was falsch?



  • trple-m schrieb:

    net schrieb:

    btw: threads und oop passen irgendwie nicht richtig zusammen...

    warum sollen threads und oop nicht zusammen passen...?

    naja, man muss irgendwie die lebenszeit des objekts und die laufzeit des threads zusammenbringen. das kann schwierig werden: kille ich im destructor den thread? soll der thread nach beendigung das objekt automatisch löschen? wenn ja, wie lösche is es, wenn es nicht auf dem heap angelegt wurde? ausserdem haste immer so'ne statische funktion als verteiler, weil die mutithreading apis mit 'thiscall' nicht klarkommen.

    btw: am besten du nimmst schon was fertiges, eine 'thread pool' klasse z.b. einfach mal danach googlen....



  • @net:

    Ich finde, daß OOP und Threads hervorragend zusammen passen. Ohne OOP mußt Du Dir überlegen, wo Du Deine Thread-spezifischen Daten unterbringst. Das kann man mit einer C++-Klasse elegant lösen.

    Daß das nicht ganz trivial ist, ist richtig, aber die OOP ermöglicht dir, diese Komplexität in einer einfach zu kapseln, so daß Du dann nur noch von einer fertigen Thread-Klasse ableitest.

    Ob man diese Thread-Klasse selbst schreibt oder ob man was fertiges nimmt, ist jedem selbst überlassen. Ich habe es selbst geschrieben und unter LGPL frei gegeben. Das Resultat kannst Du in meinen Beiträgen hier studieren.

    Ich finde, daß OOP das ganze einfacher macht.

    Tntnet


Anmelden zum Antworten