🔝 Retour au Sommaire
En Delphi, la création de threads repose principalement sur la classe TThread. C'est une classe abstraite qui encapsule toute la complexité de la gestion des threads au niveau du système d'exploitation.
Pour créer votre propre thread, vous devez :
- Créer une classe qui hérite de
TThread - Redéfinir la méthode
Execute - Instancier et démarrer votre thread
Voici la structure minimale d'un thread personnalisé :
type
TMonThread = class(TThread)
protected
procedure Execute; override;
end;
implementation
procedure TMonThread.Execute;
begin
// Le code qui s'exécutera dans le thread
// Cette méthode est appelée automatiquement au démarrage du thread
end;Créons un thread qui effectue un comptage simple :
type
TThreadCompteur = class(TThread)
private
FCompteur: Integer;
protected
procedure Execute; override;
public
constructor Create;
end;
implementation
constructor TThreadCompteur.Create;
begin
// Appeler le constructeur parent
// False = le thread démarre immédiatement
// True = le thread est créé en pause
inherited Create(False);
// Initialisation
FCompteur := 0;
// Le thread se libérera automatiquement à la fin
FreeOnTerminate := True;
end;
procedure TThreadCompteur.Execute;
var
i: Integer;
begin
// Ce code s'exécute dans un thread séparé
for i := 1 to 100 do
begin
// Vérifier si on demande l'arrêt du thread
if Terminated then
Exit;
Inc(FCompteur);
// Petite pause pour simuler un traitement
Sleep(100); // 100 millisecondes
end;
end;Pour utiliser ce thread dans votre application :
procedure TForm1.Button1Click(Sender: TObject);
var
MonThread: TThreadCompteur;
begin
// Créer et démarrer le thread
MonThread := TThreadCompteur.Create;
// Le thread démarre automatiquement car on a passé False au constructeur
end;Deux façons de créer un thread :
// Démarrage immédiat
MonThread := TMonThread.Create(False);
// Création en pause (il faudra appeler Start)
MonThread := TMonThread.Create(True);
MonThread.Start; // Démarrage manuel La méthode Execute contient le code qui s'exécutera dans le thread. Elle est appelée automatiquement lorsque le thread démarre.
procedure TMonThread.Execute;
begin
// Votre code ici
// Cette méthode s'exécute dans un thread séparé
// Boucle typique avec vérification d'arrêt
while not Terminated do
begin
// Faire quelque chose
// ...
Sleep(100); // Pause pour ne pas saturer le CPU
end;
end;Pour arrêter proprement un thread :
procedure TForm1.Button2Click(Sender: TObject);
begin
if Assigned(MonThread) then
begin
// Demander l'arrêt du thread
MonThread.Terminate;
// Attendre que le thread se termine (optionnel)
// ATTENTION : peut bloquer l'interface !
// MonThread.WaitFor;
end;
end;Deux approches pour libérer la mémoire :
Approche 1 : Libération automatique
constructor TMonThread.Create;
begin
inherited Create(False);
FreeOnTerminate := True; // Le thread se libère tout seul
end;Approche 2 : Libération manuelle
constructor TMonThread.Create;
begin
inherited Create(False);
FreeOnTerminate := False; // On gère manuellement
end;
// Plus tard, dans votre code
MonThread.Terminate;
MonThread.WaitFor; // Attendre la fin
MonThread.Free; // Libérer manuellement RAPPEL IMPORTANT : Un thread ne peut PAS modifier directement l'interface utilisateur !
Synchronize permet d'exécuter du code dans le thread principal de manière sécurisée :
type
TThreadTelecharge = class(TThread)
private
FProgression: Integer;
FMessage: string;
procedure MettreAJourInterface;
protected
procedure Execute; override;
end;
procedure TThreadTelecharge.MettreAJourInterface;
begin
// Ce code s'exécute dans le thread principal
// On peut modifier l'interface en toute sécurité
Form1.ProgressBar1.Position := FProgression;
Form1.Label1.Caption := FMessage;
end;
procedure TThreadTelecharge.Execute;
var
i: Integer;
begin
for i := 1 to 100 do
begin
if Terminated then Exit;
// Simulation de travail
Sleep(50);
// Préparer les données à afficher
FProgression := i;
FMessage := Format('Progression : %d%%', [i]);
// Demander au thread principal de mettre à jour l'interface
Synchronize(MettreAJourInterface);
end;
FMessage := 'Terminé !';
Synchronize(MettreAJourInterface);
end;Similaire à Synchronize, mais asynchrone :
procedure TMonThread.Execute;
begin
// Queue ne bloque pas le thread
// La mise à jour se fera "quand le thread principal aura le temps"
Queue(MettreAJourInterface);
// Le code continue immédiatement
// ...
end;Différence entre Synchronize et Queue :
- Synchronize : Bloque le thread secondaire jusqu'à ce que l'interface soit mise à jour
- Queue : Le thread secondaire continue sans attendre
Indique si on a demandé l'arrêt du thread :
procedure TMonThread.Execute;
begin
while not Terminated do
begin
// Traitement
// Vérifier régulièrement Terminated pour pouvoir s'arrêter proprement
end;
end;Détermine si le thread se libère automatiquement :
constructor TMonThread.Create;
begin
inherited Create(False);
FreeOnTerminate := True; // Libération automatique
end;Attention : Si FreeOnTerminate = True, ne gardez PAS de référence au thread et ne tentez pas de le libérer manuellement !
Définit la priorité du thread :
constructor TMonThread.Create;
begin
inherited Create(False);
Priority := tpNormal; // Priorité par défaut
end;Valeurs possibles :
tpIdle: Priorité très bassetpLowest: Priorité bassetpLower: Légèrement en dessous de normaltpNormal: Priorité normale (par défaut)tpHigher: Légèrement au-dessus de normaltpHighest: Priorité hautetpTimeCritical: Priorité critique (à utiliser avec précaution !)
type
TThreadCalcul = class(TThread)
private
FValeur1: Integer;
FValeur2: Integer;
FResultat: Integer;
protected
procedure Execute; override;
public
constructor Create(AValeur1, AValeur2: Integer);
property Resultat: Integer read FResultat;
end;
constructor TThreadCalcul.Create(AValeur1, AValeur2: Integer);
begin
inherited Create(True); // Créer en pause
FValeur1 := AValeur1;
FValeur2 := AValeur2;
FreeOnTerminate := False; // On veut récupérer le résultat
end;
procedure TThreadCalcul.Execute;
begin
// Simulation d'un calcul long
Sleep(2000);
FResultat := FValeur1 + FValeur2;
end;
// Utilisation
procedure TForm1.ButtonClick(Sender: TObject);
var
ThreadCalcul: TThreadCalcul;
begin
ThreadCalcul := TThreadCalcul.Create(10, 20);
ThreadCalcul.Start;
ThreadCalcul.WaitFor; // Attendre la fin
ShowMessage('Résultat : ' + IntToStr(ThreadCalcul.Resultat));
ThreadCalcul.Free;
end;type
TThreadTraitement = class(TThread)
private
FFichier: string;
protected
procedure Execute; override;
public
property Fichier: string read FFichier write FFichier;
end;
// Utilisation
MonThread := TThreadTraitement.Create(True);
MonThread.Fichier := 'C:\data.txt';
MonThread.Start; Les exceptions dans un thread doivent être gérées avec soin :
procedure TMonThread.Execute;
begin
try
// Code qui peut lever une exception
// ...
except
on E: Exception do
begin
// Enregistrer l'erreur dans un log
// Ou notifier l'utilisateur via Synchronize
end;
end;
end;procedure TMonThread.Execute;
begin
while not Terminated do
begin
// Traitement
end;
end;Évitez les boucles infinies sans condition d'arrêt :
// ❌ MAUVAIS
while True do
begin
// ...
end;
// ✅ BON
while not Terminated do
begin
// ...
end;Si FreeOnTerminate = False, n'oubliez pas de libérer le thread :
try
MonThread := TMonThread.Create(False);
MonThread.WaitFor;
finally
MonThread.Free;
end;Ne modifiez pas les mêmes données depuis plusieurs threads sans protection (nous verrons la synchronisation dans la section suivante).
Créer trop de threads peut ralentir votre application. Le système d'exploitation doit gérer chaque thread, ce qui a un coût.
type
TThreadTelechargement = class(TThread)
private
FNomFichier: string;
FProgression: Integer;
procedure AfficherProgression;
protected
procedure Execute; override;
public
constructor Create(const ANomFichier: string);
end;
constructor TThreadTelechargement.Create(const ANomFichier: string);
begin
inherited Create(False);
FNomFichier := ANomFichier;
FreeOnTerminate := True;
end;
procedure TThreadTelechargement.AfficherProgression;
begin
Form1.ProgressBar1.Position := FProgression;
Form1.Label1.Caption := Format('Téléchargement de %s : %d%%',
[FNomFichier, FProgression]);
end;
procedure TThreadTelechargement.Execute;
var
i: Integer;
begin
for i := 0 to 100 do
begin
if Terminated then Exit;
// Simulation du téléchargement
Sleep(50);
FProgression := i;
Synchronize(AfficherProgression);
end;
// Notification de fin
FProgression := 100;
Synchronize(
procedure
begin
Form1.Label1.Caption := 'Téléchargement terminé !';
ShowMessage('Le fichier ' + FNomFichier + ' a été téléchargé.');
end
);
end;- Toujours hériter de
TThreadpour créer un thread personnalisé - Redéfinir la méthode
Executequi contient le code du thread - Utiliser
SynchronizeouQueuepour mettre à jour l'interface utilisateur - Vérifier régulièrement
Terminatedpour permettre l'arrêt propre du thread - Choisir entre
FreeOnTerminate = True(automatique) ou False (manuel) - Gérer les exceptions dans la méthode
Execute - Ne jamais modifier l'interface directement depuis un thread secondaire
Dans la prochaine section, nous verrons comment synchroniser l'accès aux ressources partagées entre plusieurs threads.