Auf Beendigung der Thread-Ausführung warten
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:
- Verwenden Sie die Methode WaitFor des anderen Threads.
- 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:
- Erstellen Sie ein TEvent-Objekt mit einem globalen Gültigkeitsbereich.
- Sobald ein Thread eine Operation beendet, auf die andere Threads warten, soll der betreffende Thread die Methode TEvent.SetEvent aufrufen.
- 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:
- Implementieren Sie in der Prozedur Execute die Methode Terminate, sodass diese die Eigenschaft Terminated überprüft und entsprechend reagiert.
- 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. |