Synchronisieren von Threads und GUI in einer Delphi-Anwendung

Mit Multithreading in Delphi können Sie Anwendungen erstellen, die mehrere gleichzeitige Ausführungspfade enthalten.

Eine normale Delphi-Anwendung ist eine Single-Thread-Anwendung. Das bedeutet, dass alle VCL-Objekte auf ihre Eigenschaften zugreifen und ihre Methoden innerhalb dieses einzelnen Threads ausführen. Fügen Sie einen oder mehrere sekundäre Threads hinzu, um die Datenverarbeitung in Ihrer Anwendung zu beschleunigen.

Prozessorthreads

EIN Faden ist ein Kommunikationskanal von einer Anwendung zu einem Prozessor. Singlethread-Programme müssen während der Ausführung in beide Richtungen (zum und vom Prozessor) kommunizieren können. Multithread-Apps können mehrere verschiedene Kanäle öffnen und so die Ausführung beschleunigen.

Themen & GUI

Wenn in der Anwendung mehrere Threads ausgeführt werden, stellt sich die Frage, wie Sie Ihre grafische Benutzeroberfläche als Ergebnis einer Thread-Ausführung aktualisieren können. Die Antwort liegt in der TThread-Klasse Synchronisieren Methode.

Um die Benutzeroberfläche oder den Haupt-Thread Ihrer Anwendung von einem sekundären Thread aus zu aktualisieren, müssen Sie die Synchronize-Methode aufrufen. Diese Technik ist eine thread-sichere Methode, die Multi-Threading-Konflikte vermeidet, die durch den Zugriff auf Objekteigenschaften oder nicht thread-sichere Methoden oder durch die Verwendung von Ressourcen entstehen können, die sich nicht im Hauptthread der Ausführung befinden.

Unten sehen Sie ein Beispiel-Demo, das mehrere Schaltflächen mit Fortschrittsbalken verwendet, wobei jeder Fortschrittsbalken den aktuellen "Status" der Thread-Ausführung anzeigt.

unit MainU;
Schnittstelle
Verwendet
Windows, Nachrichten, SysUtils, Varianten, Klassen, Grafiken, Steuerelemente, Formulare,
Dialoge, ComCtrls, StdCtrls, ExtCtrls;
Art
// Interceptor-Klasse
TButton = Klasse (StdCtrls.TButton)
OwnedThread: TThread;
ProgressBar: TProgressBar;
Ende;
TMyThread = Klasse (TThread)
Privat
FCounter: Integer;
FCountTo: Integer;
FProgressBar: TProgressBar;
FOwnerButton: TButton;
procedure DoProgress;
procedure SetCountTo (const Value: Integer);
procedure SetProgressBar (const Value: TProgressBar);
procedure SetOwnerButton (const Value: TButton);
geschützt
procedure Execute; überschreiben;
Öffentlichkeit
Konstruktor Create (CreateSuspended: Boolean);
property CountTo: Integer read FCountTo write SetCountTo;
Eigenschaft ProgressBar: TProgressBar read FProgressBar write SetProgressBar;
property OwnerButton: TButton read FOwnerButton write SetOwnerButton;
Ende;
TMainForm = Klasse (TForm)
Button1: TButton;
ProgressBar1: TProgressBar;
Button2: TButton;
ProgressBar2: TProgressBar;
Button3: TButton;
ProgressBar3: TProgressBar;
Button4: TButton;
ProgressBar4: TProgressBar;
Button5: TButton;
ProgressBar5: TProgressBar;
procedure Button1Click (Sender: TObject);
Ende;
var
MainForm: TMainForm;
Implementierung
$ R * .dfm
TMyThread
Konstruktor TMyThread.Create (CreateSuspended: Boolean);
Start
vererbt;
FCounter: = 0;
FCountTo: = MAXINT;
Ende;
procedure TMyThread.DoProgress;
var
PctDone: Erweitert;
Start
PctDone: = (FCounter / FCountTo);
FProgressBar.Position: = Round (FProgressBar.Step * PctDone);
FOwnerButton.Caption: = FormatFloat ('0,00%', PctDone * 100);
Ende;
procedure TMyThread.Execute;
const
Intervall = 1000000;
Start
FreeOnTerminate: = True;
FProgressBar.Max: = FCountTo div Interval;
FProgressBar.Step: = FProgressBar.Max;
während FCounter < FCountTo do
Start
wenn FCounter mod Interval = 0, dann Synchronize (DoProgress);
Inc (FCounter);
Ende;
FOwnerButton.Caption: = 'Start';
FOwnerButton.OwnedThread: = nil;
FProgressBar.Position: = FProgressBar.Max;
Ende;
procedure TMyThread.SetCountTo (const Value: Integer);
Start
FCountTo: = Wert;
Ende;
procedure TMyThread.SetOwnerButton (const Value: TButton);
Start
FOwnerButton: = Value;
Ende;
procedure TMyThread.SetProgressBar (const Wert: TProgressBar);
Start
FProgressBar: = Wert;
Ende;
procedure TMainForm.Button1Click (Sender: TObject);
var
aTaste: TButton;
aThread: TMyThread;
aProgressBar: TProgressBar;
Start
aButton: = TButton (Absender);
wenn nicht zugewiesen (aButton.OwnedThread) dann
Start
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';
Ende
sonst
Start
if aButton.OwnedThread.Suspended then
aButton.OwnedThread.Resume
sonst
aButton.OwnedThread.Suspend;
aButton.Caption: = 'Run';
Ende;
Ende;
Ende.

Vielen Dank an Jens Borrisholt für die Einsendung dieses Codebeispiels.