Attente de l'achèvement d'une tâche
Remonter à Attente des autres threads
Parfois, il est nécessaire d'attendre qu'un thread termine une opération au lieu d'attendre la fin de l'exécution d'un thread particulier. Pour ce faire, utilisez un objet événement. Les objets événements (System.SyncObjs.TEvent) doivent être créés avec une portée globale afin qu'ils puissent agir comme des signaux visibles pour tous les threads.
Quand un thread termine une opération dont dépendent d'autres threads, il appelle TEvent.SetEvent. SetEvent active le signal afin que les autres threads le surveillant sachent que l'opération a été achevée. Pour désactiver le signal, utilisez la méthode ResetEvent.
Par exemple, imaginons une situation dans laquelle vous devez attendre la fin de l'exécution de plusieurs threads au lieu d'un seul. Comme vous ne savez pas quel sera le dernier thread, vous ne pouvez pas utiliser la méthode WaitFor de l'un d'eux. A la place, vous pouvez faire en sorte que chaque thread incrémente un compteur à la fin de son exécution et que le dernier thread signale l'achèvement de l'exécution de tous les threads en générant un événement.
Le code suivant montre la fin du gestionnaire d'événement System.Classes.TThread.OnTerminate de tous les threads dont l'exécution doit être achevée. CounterGuard est un objet section critique global qui évite l'utilisation simultanée du compteur par plusieurs threads. Counter est une variable globale qui compte le nombre de threads dont l'exécution est achevée.
procedure TDataModule.TaskThreadTerminate(Sender: TObject);
begin
// ...
CounterGuard.Acquire; { obtain a lock on the counter }
Dec(Counter); { decrement the global counter variable }
if Counter = 0 then
Event1.SetEvent; { signal if this is the last thread }
CounterGuard.Release; { release the lock on the counter }
// ...
end;
void __fastcall TDataModule::TaskThreadTerminate(TObject *Sender) {
// ...
CounterGuard->Acquire(); // lock the counter
if (--Counter == 0) // decrement the global counter
Event1->SetEvent(); // signal if this is the last thread
CounterGuard->Release(); // release the lock on the counter
}
Le thread principal initialise la variable Counter, lance les threads de tâche et attend le signal indiquant l'achèvement de l'exécution de tous les threads en appelant la méthode WaitFor. WaitFor attend l'activation du signal pendant une durée spécifiée et renvoie l'une des valeurs du tableau suivant :
Valeurs renvoyées par WaitFor :
Valeur | Signification |
---|---|
wrSignaled |
Le signal de l'objet événement a été activé. |
wrTimeout |
La durée spécifiée s'est écoulée sans que le signal soit défini. |
wrAbandoned |
L'objet événement a été détruit avant l'écoulement de la durée spécifiée. |
wrError |
Une erreur a eu lieu pendant l'attente. |
Le code suivant montre comment le thread principal lance les threads de tâche et reprend la main lorsque leur exécution est achevée :
Event1.ResetEvent; { clear the event before launching the threads }
for i := 1 to Counter do
TaskThread.Create(False); { create and launch task threads }
if Event1.WaitFor(20000) <> wrSignaled then
raise Exception;
{ now continue with the main thread. All task threads have finished }
Event1->ResetEvent(); // clear the event before launching the threads
for (int i = 0; i < Counter; i++)
new TaskThread(false); // create and launch task threads
if (Event1->WaitFor(20000) != wrSignaled)
throw Exception;
// now continue with the main thread, all task threads have finished
Remarque : Si vous ne voulez pas cesser d'attendre un événement après un délai spécifié, transmettez à la méthode WaitFor une valeur de paramètre INFINITE. Faites attention en utilisant INFINITE, car cela peut provoquer le blocage du thread si le signal attendu n'arrive pas.