🔝 Retour au Sommaire
TTask est une approche moderne et simplifiée du multithreading introduite dans Delphi. Elle fait partie de la Parallel Programming Library (PPL), qui permet de créer des applications parallèles sans gérer manuellement les threads.
| TThread | TTask |
|---|---|
| Gestion manuelle des threads | Gestion automatique |
| Code plus verbeux | Code plus concis |
| Créer une classe pour chaque tâche | Code anonyme (inline) possible |
| Gestion manuelle du pool de threads | Pool de threads intégré |
TThread reste utile pour les threads de longue durée, tandis que TTask excelle pour les tâches courtes et ponctuelles.
Imaginez que vous devez embaucher des personnes pour différents travaux :
- TThread : Vous embauchez des employés permanents. Vous gérez leurs contrats, leurs horaires, leur formation.
- TTask : Vous faites appel à des intérimaires via une agence. L'agence gère tout, vous donnez juste les tâches à accomplir.
Pour utiliser TTask, vous devez inclure l'unité System.Threading :
uses
System.Threading;procedure TForm1.ButtonClick(Sender: TObject);
begin
// Créer et démarrer une tâche
TTask.Run(
procedure
begin
// Ce code s'exécute dans un thread séparé
Sleep(2000); // Simuler un travail de 2 secondes
// Pour mettre à jour l'interface, utiliser TThread.Queue
TThread.Queue(nil,
procedure
begin
ShowMessage('Tâche terminée !');
end
);
end
);
// Le code continue immédiatement ici
// L'interface reste réactive
end;C'est tout ! Vous venez de créer votre première tâche parallèle avec seulement quelques lignes de code.
La méthode la plus simple :
TTask.Run(
procedure
begin
// Votre code ici
end
);Pour créer une tâche sans la démarrer immédiatement :
var
MaTache: ITask;
begin
// Créer la tâche
MaTache := TTask.Create(
procedure
begin
// Votre code ici
end
);
// Démarrer plus tard
MaTache.Start;
end;Pour obtenir un résultat d'une tâche :
var
Resultat: IFuture<Integer>;
begin
// Créer une tâche qui retourne un entier
Resultat := TTask.Future<Integer>(
function: Integer
begin
Sleep(2000); // Calcul long
Result := 42; // Le résultat
end
);
// Faire autre chose pendant que la tâche s'exécute
// ...
// Récupérer le résultat (attend la fin de la tâche si nécessaire)
ShowMessage('Résultat : ' + IntToStr(Resultat.Value));
end;var
MaTache: ITask;
begin
MaTache := TTask.Run(
procedure
begin
Sleep(3000);
end
);
// Attendre que la tâche se termine
MaTache.Wait;
ShowMessage('La tâche est terminée !');
end;var
MaTache: ITask;
begin
MaTache := TTask.Run(
procedure
begin
Sleep(5000);
end
);
// Attendre maximum 2 secondes
if MaTache.Wait(2000) then
ShowMessage('Tâche terminée à temps')
else
ShowMessage('Timeout ! La tâche continue en arrière-plan');
end;var
Taches: array[0..2] of ITask;
i: Integer;
begin
// Créer plusieurs tâches
for i := 0 to 2 do
begin
Taches[i] := TTask.Run(
procedure
var
Numero: Integer;
begin
Numero := i;
Sleep(1000 * (Numero + 1)); // Temps différent pour chaque tâche
TThread.Queue(nil,
procedure
begin
Memo1.Lines.Add('Tâche ' + IntToStr(Numero) + ' terminée');
end
);
end
);
end;
// Attendre que TOUTES les tâches soient terminées
TTask.WaitForAll(Taches);
ShowMessage('Toutes les tâches sont terminées !');
end;var
Taches: array[0..2] of ITask;
Indice: Integer;
begin
// Créer plusieurs tâches
// ...
// Attendre que N'IMPORTE QUELLE tâche se termine
Indice := TTask.WaitForAny(Taches);
ShowMessage('La tâche ' + IntToStr(Indice) + ' est la première terminée !');
end;Une des fonctionnalités les plus puissantes de la PPL : paralléliser automatiquement les boucles.
uses
System.Threading;
procedure TForm1.ButtonClick(Sender: TObject);
var
i: Integer;
Temps: TDateTime;
begin
Temps := Now;
// Boucle parallèle : les itérations s'exécutent en parallèle
TParallel.For(1, 100,
procedure(Index: Integer)
begin
// Chaque itération peut s'exécuter dans un thread différent
// Traitement sur l'élément Index
Sleep(50); // Simuler un traitement
end
);
ShowMessage('Terminé en ' +
FormatDateTime('ss.zzz', Now - Temps) + ' secondes');
end;// SÉQUENTIEL (lent)
procedure TraitementSequentiel;
var
i: Integer;
begin
for i := 1 to 1000 do
begin
TraiterElement(i); // S'exécute l'un après l'autre
end;
end;
// PARALLÈLE (rapide sur multi-cœurs)
procedure TraitementParallele;
begin
TParallel.For(1, 1000,
procedure(Index: Integer)
begin
TraiterElement(Index); // Plusieurs éléments traités simultanément
end
);
end;Vous pouvez contrôler le comportement de la boucle parallèle :
var
Options: TParallel.TLoopOptions;
begin
Options := TParallel.TLoopOptions.Create;
Options.MaxWorkers := 4; // Limiter à 4 threads
TParallel.For(1, 1000, Options,
procedure(Index: Integer)
begin
TraiterElement(Index);
end
);
end;Pour parcourir les éléments d'une liste en parallèle :
var
Liste: TList<string>;
begin
Liste := TList<string>.Create;
try
// Remplir la liste
Liste.Add('Fichier1.txt');
Liste.Add('Fichier2.txt');
Liste.Add('Fichier3.txt');
// Traiter chaque élément en parallèle
TParallel.ForEach<string>(Liste,
procedure(const Element: string)
begin
TraiterFichier(Element);
end
);
finally
Liste.Free;
end;
end;var
MaTache: ITask;
Token: ICancellationToken;
begin
// Créer un token d'annulation
Token := TCancellationTokenSource.Create.Token;
MaTache := TTask.Run(
procedure
var
i: Integer;
begin
for i := 1 to 1000 do
begin
// Vérifier si l'annulation est demandée
if Token.IsCancelled then
begin
TThread.Queue(nil,
procedure
begin
ShowMessage('Tâche annulée !');
end
);
Exit;
end;
// Travail
Sleep(10);
end;
end
);
// Plus tard, pour annuler :
// Token.Cancel;
end;Le pool de threads gère automatiquement un ensemble de threads réutilisables.
uses
System.Threading;
// Définir le nombre minimum et maximum de threads
TThreadPool.Default.SetMinWorkerThreads(2);
TThreadPool.Default.SetMaxWorkerThreads(8); - Performance : Réutilise les threads au lieu d'en créer de nouveaux
- Gestion automatique : Ajuste le nombre de threads selon la charge
- Simplicité : Vous n'avez rien à gérer manuellement
type
TForm1 = class(TForm)
Button1: TButton;
ProgressBar1: TProgressBar;
Label1: TLabel;
procedure Button1Click(Sender: TObject);
end;
procedure TForm1.Button1Click(Sender: TObject);
var
Fichiers: TArray<string>;
NbTraites: Integer;
CS: TCriticalSection;
begin
// Liste de fichiers à traiter
Fichiers := TDirectory.GetFiles('C:\Images', '*.jpg');
NbTraites := 0;
CS := TCriticalSection.Create;
try
ProgressBar1.Max := Length(Fichiers);
ProgressBar1.Position := 0;
// Traiter les images en parallèle
TParallel.For(0, Length(Fichiers) - 1,
procedure(Index: Integer)
begin
// Traiter l'image (redimensionner, convertir, etc.)
TraiterImage(Fichiers[Index]);
// Incrémenter le compteur de manière thread-safe
CS.Enter;
try
Inc(NbTraites);
finally
CS.Leave;
end;
// Mettre à jour l'interface
TThread.Queue(nil,
procedure
begin
ProgressBar1.Position := NbTraites;
Label1.Caption := Format('Traité : %d / %d',
[NbTraites, Length(Fichiers)]);
end
);
end
);
ShowMessage('Toutes les images ont été traitées !');
finally
CS.Free;
end;
end;var
Future1, Future2, Future3: IFuture<Integer>;
Total: Integer;
begin
// Lancer trois calculs en parallèle
Future1 := TTask.Future<Integer>(
function: Integer
begin
Sleep(1000);
Result := 10 * 10;
end
);
Future2 := TTask.Future<Integer>(
function: Integer
begin
Sleep(1000);
Result := 20 * 20;
end
);
Future3 := TTask.Future<Integer>(
function: Integer
begin
Sleep(1000);
Result := 30 * 30;
end
);
// Récupérer les résultats (attend que toutes les tâches soient terminées)
Total := Future1.Value + Future2.Value + Future3.Value;
ShowMessage('Total : ' + IntToStr(Total)); // 10 + 400 + 900 = 1410
end;Les exceptions dans les tâches doivent être gérées avec soin :
var
MaTache: ITask;
begin
MaTache := TTask.Run(
procedure
begin
try
// Code qui peut lever une exception
raise Exception.Create('Erreur dans la tâche !');
except
on E: Exception do
begin
// Logger l'erreur ou notifier l'utilisateur
TThread.Queue(nil,
procedure
begin
ShowMessage('Erreur : ' + E.Message);
end
);
end;
end;
end
);
end;var
MaTache: ITask;
begin
MaTache := TTask.Run(
procedure
begin
// ...
end
);
// Attendre la fin
MaTache.Wait;
// Vérifier le statut
case MaTache.Status of
TTaskStatus.Created:
ShowMessage('Tâche créée');
TTaskStatus.Running:
ShowMessage('Tâche en cours');
TTaskStatus.Completed:
ShowMessage('Tâche terminée avec succès');
TTaskStatus.Canceled:
ShowMessage('Tâche annulée');
TTaskStatus.Exception:
ShowMessage('Tâche terminée avec erreur');
end;
end;// ✅ BON : Tâche ponctuelle
TTask.Run(
procedure
begin
TelechargerFichier('http://example.com/data.json');
end
);
// ❌ MAUVAIS : Utiliser TThread pour les services de longue durée
// (comme surveiller un port réseau en continu)// ❌ DANGEREUX
procedure TForm1.ButtonClick(Sender: TObject);
var
i: Integer;
begin
for i := 1 to 10 do
begin
TTask.Run(
procedure
begin
// i peut avoir changé entre le moment de création et l'exécution !
ShowMessage(IntToStr(i));
end
);
end;
end;
// ✅ CORRECT : Capturer dans une variable locale
procedure TForm1.ButtonClick(Sender: TObject);
var
i: Integer;
begin
for i := 1 to 10 do
begin
TTask.Run(
procedure
var
Numero: Integer;
begin
Numero := i; // Copie locale
ShowMessage(IntToStr(Numero));
end
);
end;
end;Même avec TTask, vous devez protéger les accès concurrents :
var
CS: TCriticalSection;
Compteur: Integer;
begin
CS := TCriticalSection.Create;
try
TParallel.For(1, 1000,
procedure(Index: Integer)
begin
CS.Enter;
try
Inc(Compteur); // Protégé
finally
CS.Leave;
end;
end
);
finally
CS.Free;
end;
end;// ❌ MAUVAIS : Créer 10000 tâches d'un coup
for i := 1 to 10000 do
TTask.Run(procedure begin ... end);
// ✅ BON : Utiliser TParallel.For qui gère le pool
TParallel.For(1, 10000,
procedure(Index: Integer)
begin
// ...
end
);| Critère | TThread | TTask |
|---|---|---|
| Complexité | Plus complexe | Plus simple |
| Verbosité | Nécessite une classe | Code inline possible |
| Gestion mémoire | Manuelle | Automatique |
| Pool de threads | Non | Oui |
| Idéal pour | Threads de longue durée | Tâches ponctuelles |
| Parallélisation | Manuelle | Automatique (TParallel) |
| Courbe d'apprentissage | Plus longue | Plus courte |
- TTask simplifie grandement la programmation parallèle en Delphi
- Utilisez
TTask.Runpour les tâches simples et ponctuelles TTask.Futurepermet de récupérer un résultat d'une tâcheTParallel.Forparallélise automatiquement les boucles sur les processeurs multi-cœurs- Le pool de threads gère automatiquement la réutilisation des threads
- Toujours protéger les accès aux ressources partagées
- Utiliser
TThread.QueueouTThread.Synchronizepour mettre à jour l'interface - TTask est idéal pour les tâches courtes, TThread pour les services de longue durée
- Attention à la capture des variables dans les procédures anonymes
TTask et la Parallel Programming Library rendent la programmation parallèle accessible à tous, même aux débutants. C'est l'approche recommandée pour la plupart des besoins de multithreading dans les applications Delphi modernes.