Auf Beendigung der Thread-Ausführung warten

Aus RAD Studio
Wechseln zu: Navigation, Suche

Nach oben zu So erstellen Sie Multithread-Anwendungen

Das Warten auf die Beendigung von Threads umfasst folgende Schritte:

  • Warten, bis ein Thread vollständig ausgeführt ist.
  • Warten, bis eine Aufgabe beendet ist.
  • Überprüfen, ob ein anderer Thread auf die Beendigung des Threads wartet.

So warten Sie auf die Beendigung der Ausführung eines Threads:

  1. Verwenden Sie die Methode WaitFor des anderen Threads.
  2. Fügen Sie die erforderliche Logik ein. Im folgenden Quelltext wird beispielsweise abgewartet, bis ein anderer Thread ein ThreadList-Objekt mit Daten gefüllt hat, bevor auf die in der Liste enthaltenen Objekte zugegriffen wird:
 if ListFillingThread.WaitFor then
 begin
   with ThreadList1.LockList do
   begin
    for I := 0 to Count - 1 do
       ProcessItem(Items[I];
   end;
   ThreadList1.UnlockList;
 end;
 if( ListFillingThread->WaitFor() ) {
   TList* list = ThreadList1>LockList();
   for( int i = 0; i < list->Count; i++ ) {
     DoSomething( list->Items[i] );
   }
   ThreadList1>UnlockList();
 }


So warten Sie auf die Erledigung einer Aufgabe:

  1. Erstellen Sie ein TEvent-Objekt mit einem globalen Gültigkeitsbereich.
  2. Sobald ein Thread eine Operation beendet, auf die andere Threads warten, soll der betreffende Thread die Methode TEvent.SetEvent aufrufen.
  3. Um das Signal zu deaktivieren, fügen Sie einen Aufruf der Methode TEvent.ResetEvent ein.

Das folgende Beispiel zeigt eine Behandlungsroutine für das Ereignis OnTerminate, die mit Hilfe einer globalen Zählervariablen in einem kritischen Abschnitt die Anzahl der Threads protokolliert, deren Ausführung beendet ist. Sobald Counter den Wert 0 hat, ruft die Behandlungsroutine die Methode SetEvent auf, um zu signalisieren, dass alle Prozesse beendet wurden:



 procedure TDataModule.TaskTerminateThread(Sender: TObject);
 begin
   ...
   CounterGuard.Acquire; { Zähler mit einer Sperre belegen }
   Dec(Counter);   { Wert der globalen Zählervariable verringern }
   if Counter = 0 then
     Event1.SetEvent; { Signalisieren, ob es der letzte Thread ist }
   Counter.Release; {Sperre vom Zähler entfernen}
   ...
 end;



 void __fastcall TDataModule::TaskThreadTerminate( TObject *Sender ) {
   ...
   CounterGuard->Acquire(); // Zähler mit einer Sperre belegen
   if( ––Counter == 0 )      // Zähler verringern
     Event1>SetEvent();    // Signalisieren, ob es der letzte Thread ist
   CounterGuard->Release(); // Sperre aufheben
 }



Der Haupt-Thread initialisiert die Variable Counter, startet die Aufgaben-Threads und fragt über die Methode TEvent::WaitFor den Signalzustand ab. Die Methode WaitFor wartet eine gegebene Zeitspanne auf das Signal und gibt einen der Werte zurück, die in der nachfolgenden Tabelle aufgelistet sind.

Das folgende Codebeispiel zeigt, wie der Haupt-Thread die Aufgaben-Threads startet und seine Ausführung fortsetzt, wenn die Ausführung dieser Threads beendet wurde.



 Event1.ResetEvent(); { Ereignis vor der Ausführung der Threads zurücksetzen }
 for i := 1 to Counter do
   TaskThread.Create(False); { Aufgaben-Threads erzeugen und ausführen }
 if Event1.WaitFor(20000) <> wrSignaled then
   raise Exception;
 { Haupt-Thread fortsetzen }



 Event1>ResetEvent();  // Ereignis löschen, bevor die Threads gestartet werden
 for( int i = 0; i < Counter; i++ ) {
   new TaskThread( false );
 if( Event1>WaitFor(20000) != wrSignaled )
   throw Exception;
 // nun den Haupt-Thread fortsetzen



Anmerkung:  Wenn das Warten auf eine Ereignisbehandlungsroutine nicht auf eine bestimmte Zeitspanne beschränkt sein soll, übergeben Sie der Methode WaitFor den Wert INFINITE als Parameter. Allerdings ist hier Vorsicht geboten, denn wenn das Signal aus irgendwelchen Gründen nie empfangen wird, bleibt der Thread einfach stehen.

So überprüfen Sie, ob ein anderer Thread auf die Beendigung Ihres Threads wartet:

  1. Implementieren Sie in der Prozedur Execute die Methode Terminate, sodass diese die Eigenschaft Terminated überprüft und entsprechend reagiert.
  2. Diese Logik kann beispielsweise wie folgt programmiert werden:
 procedure TMyThread.Execute;
 begin
   while not Terminated do
     PerformSomeTask;
 end;
 void __fastcall TMyThread::Execute() {
   while( !Terminated )
     DoSomething();
 }


Rückgabewerte von WaitFor:



Wert

Beschreibung

wrSignaled

Das Signal des Ereignisses war gesetzt.

wrTimeout

Die festgelegte Zeitspanne ist verstrichen, ohne dass das Signal gesetzt wurde.

wrAbandoned

Das Ereignisobjekt wurde freigegeben, bevor das Timeout-Intervall abgelaufen war.

wrError

Beim Warten ist ein Fehler aufgetreten.



Siehe auch