Restart eines Threads



  • Jetzt ist mir gerade noch was aufgefallen: Wenn ich FreeOnTerminaste auf True setze kriege ich beim erneuten Start eine Fehlermeldung. Setze ich den Wert auf false, kann ich den Start-Button drücken. Es geschieht zwar nichts aber er stürzt auch nicht ab. Kann es sein, dass der Thread zwar terminiert worden ist, aber noch irgendwo zu finden ist ?? Oder initialisiere ich den Thread vielleicht falsch ??
    Ich mache es mit dem BCB. Zuerst ne Anwendung und dann ein Thread-Objekt hinzufügen.

    CU GF



  • Hi,
    du erzeugst einen neuen thread nur, wenn !MyThread also wenn MyThread == NULL.
    Zwar ist FreeOnTerminate = true, aber es kann sein dass trotzdem nicht NULL in der Variablen steht. Vielleicht nutzt es dir was wenn du die Variable nach dem beenden des Threads auf NULL setzt. Vielleicht gibt es auch noch bessere Wege
    CU



  • if(MyThread!=NULL)
      {
        Application->MessageBoxA("Fehler","Error",MB_OK);
        return;
      }
    

    Und tatsächlich erscheint bei erneutem Startversuch die Messagebox. d.h. es muss noch irgendwo irgendwas drin stehen.

    @ rincewind
    Die Idee ist gut.

    Habe jetzt MyThread=NULL gesetzt, also direkt nach dem Terminate Befehl. Jetzt lässt sich der Start Button wieder drücken und erzeugt einen neuen Thread. Ohne Fehlermeldung.
    Allerdings funktionieren die Buttons Suspend und Resume nach dem Neustart nicht mehr. Vermutlich steht da noch ein Zeiger auf den alten Thread drin.
    Hat jemand ne Idee wie ich diese Adressen rausbekomme bzw. aktualisieren kann, damit der neu erzeugte Thread wieder suspendet und resumed werden kann.
    Thx

    Habe jetzt gerade festgestellt, dass die Idee mit dem NUll setzen jedoch nur beim ersten Mal funktioniert. Beim zweiten ´Versuch den Thread zu unterbrechen wird wieder ene Fehlermeldung erzeugt und weiterhin wird nun für jedes Drücken des STartButtons auf einmal ein neuer Thread erzeugt.
    HILFE !!!!!



  • Hi,
    kannst dir ja auch mal das Thread-Beispiel vom BCB anschauen....eigene OnTerminate Behandlung schreiben, flag setzen und den neustart davon abhängig machen



  • Vielleicht ein paar Grundlagen zum besseren Verständnis:

    Ein Thread läuft innerhalb der Execute-Methode, wobei und das ist z.B. für Datenbankanbindungen wichtig, diese Execute-Methode in einem anderen Speicherbereich angesiedelt ist als der Main-Thread.

    void __fastcall My_TThread::Execute()
    {
        MyFunction();
    }
    

    In diesem Fall wird MyFunction einmal abgearbeitet und der Thread dann beendet.
    Möchte ich, dass der Thread mehr als einmal MyFunction() aufruft, wird das ganze z.B. in eine while-Schleife gepackt.

    void __fastcall My_TThread::Execute()
    {
         while (!Terminated) 
            MyFunction();
    
    }
    

    Nun läuft der Thread ständig bis das Flag Terminated auf true gesetzt wird (My_Thread->Terminate())
    Möchte ich, dass der Thread genau einmal MyFunction() aufruft und dann nicht! beendet sondern sich schlafen legt(Suspend), bis er wieder aufgeweckt wird (Resume) um dann erneut MyFunction() aufruft, sieht das hanze so aus:

    void __fastcall My_TThread::Execute()
    {
         while (!Terminated) 
        {
            MyFunction();
            Suspend();
        }
    }
    

    Hier kann man erst einaml erkennen, dass Terminate nichts anderes als ein Flag ist, welches mit der Methode Terminate() lediglich auf true gesetzt wird. D.h. aber auch, wenn der Thread schläft (suspended ist) passiert gar nichts. Also vor einem Terminat prüfen, ob der Thread auch läuft. Das Flag Terminated kann man bzw. sollte man in der Ausführungsmethode (hier MyFunction) innerhalb von langen while/for Schleifen immer abfragen um diese ggf. eher verlassen zu können.
    Vorausgesetzt der Thread wurde suspended erzeugt (CreateSuspended = true)
    Was, bis auf wenige Ausnahmen eigentlich immer gemacht werden sollte.
    1.) Thread aufwecken (starten). MyThread sollte in der Mainklasse (MainForm) unter private: deklariert sein und im Konstrukor auf NULL gesetzt werden

    // Header von MainForm
        private:
           TThread *MyThread 
    
    // Konstruktor der MainForm
    __fastcall TMainForm::TMainForm(TComponent* Owner)
    	: TForm(Owner)
    {
        MyThread = NULL;
    }
    

    Mit Button 1 wird der Thread ggf. neu erzeugt und gestartet
    Button_1

    if (!MyTThread)
        //es gibt noch kein ThreadObjekt also erzeuge eines
            MyThread= new TMyThread(true); 
        MyThread->Resume(); //starte den Thread
    

    2.)Thread soll beendet werden. Ich bevorzuge immer FreeOnTerminate auf false zu setzen, deshalb räume ich das Thread-Objekt selbe auf.
    Button_2

    if (MyTThread)
        {
           MyTThread->Terminate(); // setze zuerst das Flag Terminated auf true
           if (MyTThread->Suspended) // schäft mein Thread ?
               MyTThread->->Resume(); // ja, wache ihn auf, damit er sich beenden kann
    
           thread_ShowElements->WaitFor(); // warte bis thread beendet
           delete MyTThread;    // loesche das ThreadObjekt
           MyTThread = NULL;    // sollte man mit jedem geloeschten Objekt machen
        }
    

    Du kannst nun mit Button 2 den Thread beenden und mit Button 1 neu erzeugen. Um aber nur den Counter auf einen definierten Anfangswert zu setzen reicht es, wenn Du den Thread mit z.B. Button 3 auf Suspend setzt, den Counter auf einen bestimmten Wert setzt und dann mit Button_1 den Thread wieder startest

    Gruß
    Gerhard



  • Da ich einfach aus den Beispielen der werten "Frager" kopiert habe ist mir gerade beim nochmaligen Lesen aufgefallen, dass in diesen Beispielen die Thread-Klasse direkt von TThread abgeleitet wird, was nicht geht.
    Die Klasse TThread ist eine abstrakte Klasse von der man kein Threadobjekt direkt erzeugen kann. Also immer seine eigene Threadklasse von TThread ableiten:

    // im Header File
    class My_Thread : public TThread
    {
    }
    
    // Konstruktor im cpp File
    __fastcall My_Thread ::My_Thread (bool CreateSuspended)
                                       : TThread(CreateSuspended)
    {
    }
    

    Dieses bitte bei meinen vorherigen Ausführungen berücksichtigen bzw. im Header von MainForm verbessern.

    // Header von MainForm
        private:
          My_Thread * MyThread;
    

    Sorry !

    Gerhard



  • @ Gerhard

    Danke für deine Erklärungen, die waren echt gut. Auf jeden Fall läuft jetzt alles so wie gewollt.
    Wenn ich mir jedoch deinen und meinen Quelltext so ankucke ist er nicht sehr verschieden. Bis auf ein paar kleine Änderungen. z.B. die Sache mit dem eigenen CleanUpText von dir. Naja und vor allem die KLassenableitung. 😞

    Habe noch 2 Fragen:
    1.) Könnten die ersten Fehler davon kommen, dass ich Keineeigene Threadklasse abgeleitet habe ??
    2.) Ist es richtig, das der Thread bzw. die Adresse, nicht leer ist, obwohl er auf Terminated=true gesetzt wurde und FreeOnTerminate=true; was steht da noch drin ?? 😕

    Danke an Alle
    especially Gerhard 👍

    FGGF



  • hallo,

    die ausführungen von gerhardt waren schon mal recht sinnvoll und haben das programm sehr verbessert. hier kommt nun nun noch ein tip von mir: wie gerhard vorher schon erwähnte läuft ein thread in einem anderen bereich als der vcl-hauptthread. wenn man innerhalb eines threads nun auf vcl-objekte zugreift, kann das verheerende (kann muß aber nicht sofort) folgen haben. folgender code ist unsauberer programmierstil und sollte geändert werden:

    void __fastcall TMyThread::Execute() 
    { 
      while(Terminated==false)    //solange kein Terminate Signal empfangen wurde 
      {                           // Funktion Counter 
        Counter(); 
      } 
      FreeOnTerminate=true; 
    } 
    //--------------------------------------------------------------------------- 
    void TMyThread::Counter() 
    { 
      for(int i=0; i<20; i++)        // einfache Zählschleife 
      { 
        Form1->RichEdit1->Lines->Text=i; 
        Sleep(100); 
      } 
     return; 
    } 
    //---------------------------------------------------------------------------
    

    dieser code sollte folgendermaßen abgeändert werden:

    void __fastcall TMyThread::Execute() 
    { 
      FreeOnTerminate=true; //damit sogleich gesetzt und gültig.
      while(!Terminated)    
      {                               
        Synchronize(Counter); //Läuft nun mit dem vcl-hauptthread und ist ungefährlich.
      }  
    } 
    //--------------------------------------------------------------------------- 
    void TMyThread::Counter() 
    { 
      for(int i=0; i<20; i++)        // einfache Zählschleife 
      { 
        Form1->RichEdit1->Lines->Text=i; 
        Sleep(100); 
      } 
     return; 
    } 
    //---------------------------------------------------------------------------
    

    die Methode die man Synchronize übergibt läuft im context des vcl-mainthreads...

    mfg
    murph



  • Hab noch ne bessere Möglichkeit gefunden, ohne den Counter auf Null zu setzen.
    Einfach mit ner guten Implementierung der Schleife. Die jedesmal abfragt ob Terminate true ist oder net.
    Für alle dies interessiert. 2 Buttons
    Button1:

    if (!MyThread)
        //es gibt noch kein ThreadObjekt also erzeuge eines
            MyThread= new My_Thread(true);
          MyThread->Resume(); //starte den Thread
    

    Button2

    if (MyThread)
          {
             MyThread->Suspend();
             MyThread->Terminate(); // setze zuerst das Flag Terminated auf true
             if (MyThread->Suspended) // schäft mein Thread ?
                 MyThread->Resume(); // ja, wache ihn auf, damit er sich beenden kann
    
            MyThread->WaitFor(); // warte bis thread beendet
            delete MyThread;    // loesche das ThreadObjekt
            MyThread = NULL;    // sollte man mit jedem geloeschten Objekt machen
    

    und die Schleife die jedesmal auf Wahrheitsgehalt abgefragt wird

    for(i = 600; i > 0 ; i--)
      {
        if(!Terminated)
        {
          Form3->LETimer->Text=i;
          Sleep(1000);
        }
      else break;
      }
    

    Special Thx an Gerhard und Murphy. 😉

    FG GF 😮



  • Zu Deinen 2 Fragen:

    Habe noch 2 Fragen:
    1.) Könnten die ersten Fehler davon kommen, dass ich Keine eigene Threadklasse abgeleitet habe ??
    --->ja

    2.) Ist es richtig, das der Thread bzw. die Adresse, nicht leer ist, obwohl er auf Terminated=true gesetzt wurde und FreeOnTerminate=true; was steht da noch drin ??
    --->nein die Adresse ist nicht leer. Normalerweise steht dort immer noch die Adresse Deines Threads drin, der aber nicht mehr existiert und somit der Zeiger ins Nirvana zeigt.
    Deshalb sollte man auch alle Objekte die man mit new erzeugt hat und irgendwann gelöscht hat auf NULL setzen.
    Bsp.:

    int *i = new int;
    delete i;
    

    Sollte man in umfangreichen Programmen vergessen haben, dass man ein Objekt schon gelöscht hat, hat ein Zugriff ein erneuter Zugriff auf das Objekt (hier i) u.U. katastrophale Folgen. Bei einem erneuten löschen delelte i, wird man in der Regel mit einer Access vioaltion davon kommen.

    Gruß
    Gerhard



  • Gerhard schrieb:

    int *i = new int;
    delete i;
    

    Besser:

    int *i = new int;
    delete i;
    i = NULL;
    

    -junix


Anmelden zum Antworten