Synchronisation des threads et de l'interface graphique dans une application Delphi

Le multi-threading dans Delphi vous permet de créer des applications qui incluent plusieurs chemins d'exécution simultanés.

Une application Delphi normale est monothread, ce qui signifie que tous les objets VCL accèdent à leurs propriétés et exécutent leurs méthodes dans ce thread unique. Pour accélérer le traitement des données dans votre application, incluez un ou plusieurs threads secondaires.

Threads de processeur

UNE fil est un canal de communication entre une application et un processeur. Les programmes monothread ont besoin d'une communication pour circuler dans les deux directions (vers et depuis le processeur) pendant son exécution; les applications multi-thread peuvent ouvrir plusieurs canaux différents, accélérant ainsi l'exécution.

Threads & GUI

Lorsque plusieurs threads s'exécutent dans l'application, la question se pose de savoir comment mettre à jour votre interface utilisateur graphique à la suite d'une exécution de thread. La réponse réside dans la classe TThread Synchroniser méthode.

Pour mettre à jour l'interface utilisateur ou le thread principal de votre application à partir d'un thread secondaire, vous devez appeler la méthode Synchronize. Cette technique est une méthode thread-safe qui évite les conflits multi-thread qui peuvent survenir lors de l'accès aux propriétés d'objets ou aux méthodes qui ne sont pas thread-safe, ou en utilisant des ressources qui ne sont pas dans le thread principal d'exécution.

Ci-dessous est un exemple de démonstration qui utilise plusieurs boutons avec des barres de progression, chaque barre de progression affichant "l'état" actuel de l'exécution du thread.

unit MainU;
interface
les usages
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogues, ComCtrls, StdCtrls, ExtCtrls;
type
// classe d'intercepteur
TButton = classe (StdCtrls.TButton)
OwnedThread: TThread;
ProgressBar: TProgressBar;
fin;
TMyThread = classe (TThread)
privé
FCounter: Entier;
FCountTo: Entier;
FProgressBar: TProgressBar;
FOwnerButton: TButton;
procédure DoProgress;
procédure SetCountTo (const Value: Integer);
procédure SetProgressBar (const Value: TProgressBar);
procédure SetOwnerButton (const Value: TButton);
protégé
procédure Exécuter; passer outre;
Publique
constructeur Create (CreateSuspended: Boolean);
propriété CountTo: entier lu FCountTo écrit SetCountTo;
propriété ProgressBar: TProgressBar lire FProgressBar écrire SetProgressBar;
property OwnerButton: TButton read FOwnerButton write SetOwnerButton;
fin;
TMainForm = classe (TForm)
Button1: TButton;
ProgressBar1: TProgressBar;
Button2: TButton;
ProgressBar2: TProgressBar;
Button3: TButton;
ProgressBar3: TProgressBar;
Button4: TButton;
ProgressBar4: TProgressBar;
Button5: TButton;
ProgressBar5: TProgressBar;
procédure Button1Click (Sender: TObject);
fin;
var
MainForm: TMainForm;
la mise en oeuvre
$ R * .dfm
TMyThread
constructeur TMyThread.Create (CreateSuspended: Boolean);
commencer
hérité;
FCounter: = 0;
FCountTo: = MAXINT;
fin;
procedure TMyThread.DoProgress;
var
PctDone: étendu;
commencer
PctDone: = (FCounter / FCountTo);
FProgressBar.Position: = Round (FProgressBar.Step * PctDone);
FOwnerButton.Caption: = FormatFloat ('0,00%', PctDone * 100);
fin;
procedure TMyThread.Execute;
const
Intervalle = 1000000;
commencer
FreeOnTerminate: = True;
FProgressBar.Max: = FCountTo div Interval;
FProgressBar.Step: = FProgressBar.Max;
tandis que FCounter < FCountTo do
commencer
si FCounter mod Interval = 0 alors Synchronize (DoProgress);
Inc (FCounter);
fin;
FOwnerButton.Caption: = 'Start';
FOwnerButton.OwnedThread: = nil;
FProgressBar.Position: = FProgressBar.Max;
fin;
procedure TMyThread.SetCountTo (const Value: Integer);
commencer
FCountTo: = valeur;
fin;
procedure TMyThread.SetOwnerButton (const Value: TButton);
commencer
FOwnerButton: = Value;
fin;
procedure TMyThread.SetProgressBar (const Value: TProgressBar);
commencer
FProgressBar: = valeur;
fin;
procédure TMainForm.Button1Click (Sender: TObject);
var
aButton: TButton;
aThread: TMyThread;
aProgressBar: TProgressBar;
commencer
aButton: = TButton (Sender);
sinon assigné (aButton.OwnedThread) alors
commencer
aThread: = TMyThread.Create (True);
aButton.OwnedThread: = aThread;
aProgressBar: = TProgressBar (FindComponent (StringReplace (aButton.Name, 'Button', 'ProgressBar', [])));
aThread.ProgressBar: = aProgressBar;
aThread.OwnerButton: = aButton;
aThread.Resume;
aButton.Caption: = 'Pause';
fin
autre
commencer
si aButton.OwnedThread.Suspended alors
aButton.OwnedThread.Resume
autre
aButton.OwnedThread.Suspend;
aButton.Caption: = 'Run';
fin;
fin;
fin.

Merci à Jens Borrisholt d'avoir soumis cet exemple de code.