Attente des threads
Remonter à Comment construire des applications multithreads
Voici les procédures qui peuvent être utilisées pour attendre les threads.
- Attendre la fin d'exécution d'un thread
- Attendre l'achèvement d'une tâche
- Vérifier si un autre thread attend la fin de votre thread
Pour attendre la fin d'exécution d'un thread
- Utilisez la méthode WaitFor de l'autre thread.
- Codez votre logique. Par exemple, le code suivant attend qu'un autre thread remplisse un objet liste de threads avant d'accéder aux objets de la liste :
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();
}
Pour attendre l'achèvement d'une tâche
- Créez un objet TEvent de portée globale.
- Quand un thread termine une opération que d'autres threads attendent, il appelle TEvent.SetEvent.
- Pour désactiver le signal, appelez TEvent.ResetEvent.
L'exemple suivant est un gestionnaire d'événement OnTerminate qui utilise un compteur global dans une section critique pour mémoriser le nombre de threads terminés. Quand Counter atteint 0, le gestionnaire appelle la méthode SetEvent pour signaler que tous les processus sont terminés :
procedure TDataModule.TaskTerminateThread(Sender: TObject);
begin
...
CounterGuard.Acquire; {obtient un verrou sur le compteur}
Dec(Counter); { décrémente la variable globale du compteur }
if Counter = 0 then
Event1.SetEvent; { signale s'il s'agit du dernier thread }
Counter.Release; {déverrouiller le compteur}
...
end;
void __fastcall TDataModule::TaskThreadTerminate( TObject *Sender ) {
...
CounterGuard->Acquire(); // verrouille le compteur
if( ––Counter == 0 ) // décrémente le compteur
Event1->SetEvent(); // signale si c'est le dernier thread
CounterGuard->Release(); // libère le verrouillage
}
Le thread principal initialise la variable Counter, lance les threads de tâches et attend le signal indiquant l'achèvement de l'exécution de tous les threads en appelant la méthode TEvent::WaitFor. WaitFor attend l'activation du signal pendant une durée spécifiée et renvoie l'une des valeurs du tableau ci-dessous.
L'exemple de code suivant montre comment le thread principal lance les threads de tâches et reprend la main lorsque leur exécution est achevée.
Event1.ResetEvent; {efface l'événement avant de lancer les threads}
for i := 1 to Counter do
TaskThread.Create(False); {crée et lance les threads de tâches}
if Event1.WaitFor(20000) <> wrSignaled then
raise Exception;
{poursuite avec le thread principal}
Event1–>ResetEvent(); // efface l'événement avant de lancer les threads
for( int i = 0; i < Counter; i++ ) {
new TaskThread( false );
if( Event1–>WaitFor(20000) != wrSignaled )
throw Exception;
// poursuite maintenant avec le thread principal
Remarque : Si vous ne voulez pas cesser d'attendre un gestionnaire d'é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.
Pour vérifier si un autre thread attend la fin de votre thread
- Dans votre procédure Execute, implémentez la méthode Terminate en testant la valeur de la propriété Terminated.
- Voici un moyen de coder la logique :
procedure TMyThread.Execute;
begin
while not Terminated do
PerformSomeTask;
end;
void __fastcall TMyThread::Execute() {
while( !Terminated )
DoSomething();
}
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 période d'attente. |
wrError |
Une erreur a eu lieu pendant l'attente. |