Fragen zu Threads



  • Einige Fragen zu Threads (vom Typ TThread). Der Thread wird vom Hauptformular mit

    myThread = new TMyThread(true); //bzw. myThread = new TMyThread(false);
    

    aktiviert. Je nachdem ob das Argument 'true' o. 'false' ist wird der Thread dann über

    myThread->Resume();
    

    gestartet.

    Frage 1:
    Angenommen in meinem Thread werden berechnungen durchgeführt, die einige Zeit in Anspruch nehmen. Woher weiss mein Hauptformular, dass der Thread beendet ist?

    Frage 2:
    Gehen wir davon aus, dass das Hauptformular über die Beendigung des Thread informiert worden ist. Muss dann nicht der Speicher über

    delete myThread;
    

    wieder freigegeben werden, bevor ich den Thread mit

    myThread = new TMyThread(true); //bzw. myThread = new TMyThread(false);
    

    wieder starten kann?

    Frage 3:
    Angenommen ich benutze die Befehle 'Resume()' und 'Suspend()'. Was genau geschieht in dem Augenblick wenn 'Suspend()' aktiviert wird? Wird der Thread etwa angehalten - egal wo er in diesem Moment steht und dann mit 'Resume()' an gleicher Stelle fortgesetzt?

    Frage 4:
    Über 'Terminate()' kann man das Eigenschafts-Flag 'Terminated' ansprechen. In der Borland-Hilfe steht, dass man z.B. in 'Execute()' diese Eigenschaft abfragen kann und die Ausführung gegebenenfalls beenden kann. Leider steht da nicht wie man das macht. Hat jemand einen Tip?

    Danke im voraus



  • Frage1)
    gar nicht. Es sei denn, du seztz ein Flag der Aufrufenden Klasse im Destruktor des Threads oder Syncronisiert im Thread.

    Frage2)
    ja, wenn du nicht FreeOnTerminate des Threads gesetzt hast.
    Wenn FreeOnTerminate gesetzt wurde, wird die Instanz automatisch [EDIT]zerstört[/EDIT].

    Frage3)
    Suspend() setzt die Eigenschaft Suspended auf true.Wenn Suspended true ist hält den Thread an.
    Meines Wissens friert der Thread an der Stelle ein, an der er sich gerade befindet und macht später an dieser Stelle auch weiter, wenn er wieder aktivert wird.

    Der Parameter im Konstruktor setzt Suspended.

    Du kannst den Thread weiterlaufen lassen, indem du Resume() aufrufst oder Suspended auf false setzt.

    Frage4:

    while(!Terminated)
    {
    // mach was
    }
    

    In der Hilfe sowie im Example- Ordner sind aber auch Beispiele.



  • hallo,

    zu: Frage 1:
    Angenommen in meinem Thread werden berechnungen durchgeführt, die einige Zeit in Anspruch nehmen. Woher weiss mein Hauptformular, dass der Thread beendet ist?

    es gibt die möglichkeit, den methodenzeiger OnTerminate zu setzen, in dem du ihm z. b. nach dem konstruktoraufruf eine methode zuweist:

    void __fastcall TForm1::StartThreadButtonClick(TObject *Sender)
    {
       FNewThread = new TMyThread(false);
       FNewThread->OnTerminate = DeleteThread;
    }
    
    void __fastcall TForm1::DeleteThread(TObject* Sender)
    {
       //Wenn dieser Code ausgeführt wird, ist die Threadberechnung zu ende.
       FNewThread->Terminate();
    }
    

    mfg
    murph



  • hi,

    1. muss man ein mit new erzeugtes Objekt auch wieder mit delete zerstören.
    2. Terminate() im Ereignis OnTerminate aufzurufen macht kein Sinn.

    um dass mal zu untermauern hier mal die den wesentlichen Quellcode der Klasse TThread:

    im Konstruktor wird mit BeginThread eine Thread erzeugt:

    BeginThread(nil, @ThreadProc, Pointer(Self), FThreadID)
    

    Die Procedure ThreadProc sieht dabei so aus:

    function ThreadProc(Thread: TThread): Integer;
    var
      FreeThread: Boolean;
    begin
    // code, der davor kommt und für dieses Problem belanglos ist
      try
        if not Thread.Terminated then  
        try
          Thread.Execute; // Dieses ist der eigentliche Thread.
        except
          Thread.FFatalException := AcquireExceptionObject;
        end;
      finally
    
        FreeThread := Thread.FFreeOnTerminate;
        Result := Thread.FReturnValue;
        Thread.FFinished := True;
        // an dieser Stelle ist bereits das wesentliche passiert. Der eigentliche Thread läuft noch, die Aufgaben des Thread
        // welche in der Execute- Methde definiert wurden sind erledigt und der Thread wird nun gestopt.
       // zuvor wird noch das Ereignis OnTerminate aufgerufen.
       // Da der trhead noch läuft ruft DoTerminate das Ereignis OnTerminate Syncronisiert auf:
        Thread.DoTerminate;
       // wenn in dem Ereignis Terminate() aufgerufen wurde, interessiert es dem Thread recht wenig, da der Thread sowieso schon so gut wie tot ist.
       // Wenn FreeOnTerminate gesetzt wurde, wird der Thread in der nächsten Zeile zerstört. Ansonsten existert der Thread weiter, 
       //sofern du den Thread nicht in OnTerminate zerstört hast, was immer ein Problem ist (Siehe unten)
        if FreeThread then Thread.Free;
    {$IFDEF MSWINDOWS}
        EndThread(Result); // nun wird der Thread vom System abgemeldet
    {$ENDIF}
    // weiterer code
    

    nun mal murphy's code:

    void __fastcall TForm1::StartThreadButtonClick(TObject *Sender) 
    { 
       FNewThread = new TMyThread(false); 
       FNewThread->OnTerminate = DeleteThread; 
    }
    

    wir gehen mal davon aus, dass FNewThread in der Header von TForm1 deklariert ist. Dann wird von TMyThread eine neue Instanz erzeugt. Diese wird aber nirgenswo zerstört. Selbst wenn du wolltest hast du immer ein Problem. Wenn du zum Beispiel schreibst:

    void __fastcall TForm1::DeleteThread(TObject* Sender) 
    { 
       //Wenn dieser Code ausgeführt wird, ist die Threadberechnung zu ende. 
       delete FNewThread; 
       FNewThread=NULL; 
    }
    

    funktioniert es, sofern nur eine Instanz gestartet wurde.
    Was aber wenn der Thread 3 mal gestartet wurde, weil der User dreimal auf dem Button rumgetrampelt ist ?
    dann ist folgendes passiert:

    Es wurden 3 Instanzen erzeugt:

    FNewThread = new TMyThread(false); 
    FNewThread = new TMyThread(false); 
    FNewThread = new TMyThread(false);
    

    nun kommt das Ereignis On Terminate, weil die Erste Instanz fertig ist:

    void __fastcall TForm1::DeleteThread(TObject* Sender) 
    { 
       //Wenn dieser Code ausgeführt wird, ist die Threadberechnung zu ende. 
       delete FNewThread; 
       FNewThread=NULL; 
    // So, hier ist nun FNewThread gelöscht.
    }
    

    jetzt ist die zweite Instanz fertig, und das Ereignis on Terminate wird aufgerufen:

    void __fastcall TForm1::DeleteThread(TObject* Sender) 
    { 
       //Wenn dieser Code ausgeführt wird, ist die Threadberechnung zu ende. 
       delete FNewThread; // Fehler: FNewThread wurde bereits zerstört und ist NULL !
       FNewThread=NULL; 
    }
    

    die Folge sind Speicherlöcher und Zugriffsverletzungen ohne Ende.

    Lösungsvorschlag ( ist vorzuziehen)
    FreeOnTerminate auf true setzen, bevor der Thread gestartet wird.( siehe Quellcode von TThread oben )


Anmelden zum Antworten