Sincronizzazione di thread e GUI in un'applicazione Delphi

Il multi-threading in Delphi consente di creare applicazioni che includono diversi percorsi di esecuzione simultanei.

Una normale applicazione Delphi è a thread singolo, il che significa che tutti gli oggetti VCL accedono alle loro proprietà ed eseguono i loro metodi in questo singolo thread. Per velocizzare l'elaborazione dei dati nell'applicazione, includere uno o più thread secondari.

Discussioni del processore

UN filo è un canale di comunicazione da un'applicazione a un processore. I programmi a thread singolo richiedono la comunicazione per fluire in entrambe le direzioni (da e verso il processore) durante l'esecuzione; le app multi-thread possono aprire diversi canali, accelerando così l'esecuzione.

Discussioni e GUI

Quando nell'applicazione sono in esecuzione più thread, sorge la domanda su come aggiornare l'interfaccia utente grafica a seguito dell'esecuzione di un thread. La risposta sta nella classe TThread Sincronizzare metodo.

Per aggiornare l'interfaccia utente dell'applicazione o il thread principale da un thread secondario, è necessario chiamare il metodo Synchronize. Questa tecnica è un metodo thread-safe che evita conflitti multi-threading che possono derivare dall'accesso a proprietà o metodi dell'oggetto non thread-safe o dall'utilizzo di risorse non nel thread principale di esecuzione.

Di seguito è riportato un esempio di demo che utilizza diversi pulsanti con barre di avanzamento, ciascuna delle quali mostra lo "stato" corrente dell'esecuzione del thread.

unità MainU;
interfaccia
usi
Windows, Messaggi, SysUtils, Varianti, Classi, Grafica, Controlli, Moduli,
Finestre di dialogo, ComCtrls, StdCtrls, ExtCtrls;
genere
// classe intercettore
TButton = class (StdCtrls.TButton)
OwnedThread: TThread;
ProgressBar: TProgressBar;
fine;
TMyThread = class (TThread)
privato
FCounter: intero;
FCountTo: Integer;
FProgressBar: TProgressBar;
FOwnerButton: TButton;
procedura DoProgress;
procedura SetCountTo (const Value: Integer);
procedura SetProgressBar (valore const: TProgressBar);
procedura SetOwnerButton (const Valore: TButton);
protetta
procedura Eseguire; oltrepassare;
pubblico
costruttore create (CreateSuspended: Boolean);
proprietà CountTo: integer legge FCountTo scrive SetCountTo;
proprietà ProgressBar: TProgressBar leggi FProgressBar scrivi SetProgressBar;
proprietà OwnerButton: TButton legge FOwnerButton scrive SetOwnerButton;
fine;
TMainForm = class (TForm)
Button1: TButton;
ProgressBar1: TProgressBar;
Button2: TButton;
ProgressBar2: TProgressBar;
Button3: TButton;
ProgressBar3: TProgressBar;
Button4: TButton;
ProgressBar4: TProgressBar;
Button5: TButton;
ProgressBar5: TProgressBar;
procedura Button1Click (Mittente: TObject);
fine;
var
MainForm: TMainForm;
implementazione
$ R * .dfm
TMyThread
costruttore TMyThread.Create (CreateSuspended: Boolean);
inizio
ereditato;
FCounter: = 0;
FCountTo: = MAXINT;
fine;
procedura TMyThread.DoProgress;
var
PctDone: esteso;
inizio
PctDone: = (FCounter / FCountTo);
FProgressBar.Position: = Round (FProgressBar.Step * PctDone);
FOwnerButton.Caption: = FormatFloat ('0.00%', PctDone * 100);
fine;
procedura TMyThread.Execute;
const
Intervallo = 1000000;
inizio
FreeOnTerminate: = True;
FProgressBar.Max: = FCountTo div Interval;
FProgressBar.Step: = FProgressBar.Max;
mentre FCounter < FCountTo do
inizio
se FCounter mod Interval = 0 allora Synchronize (DoProgress);
Inc (FCounter);
fine;
FOwnerButton.Caption: = 'Start';
FOwnerButton.OwnedThread: = nil;
FProgressBar.Position: = FProgressBar.Max;
fine;
procedura TMyThread.SetCountTo (valore const: intero);
inizio
FCountTo: = Value;
fine;
procedura TMyThread.SetOwnerButton (const Valore: TButton);
inizio
FOwnerButton: = Value;
fine;
procedura TMyThread.SetProgressBar (valore const: TProgressBar);
inizio
FProgressBar: = Value;
fine;
procedura TMainForm.Button1Click (Mittente: TObject);
var
aButton: TButton;
aThread: TMyThread;
aProgressBar: TProgressBar;
inizio
aButton: = TButton (mittente);
se non assegnato (aButton.OwnedThread), allora
inizio
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: = 'Pausa';
fine
altro
inizio
se aButton.OwnedThread.Suspended allora
aButton.OwnedThread.Resume
altro
aButton.OwnedThread.Suspend;
aButton.Caption: = 'Esegui';
fine;
fine;
fine.

Grazie a Jens Borrisholt per aver inviato questo esempio di codice.