Threads en GUI synchroniseren in een Delphi-toepassing

Met multi-threading in Delphi kunt u applicaties maken die verschillende gelijktijdige uitvoeringspaden bevatten.

Een normale Delphi-toepassing heeft één thread, wat betekent dat alle VCL-objecten toegang hebben tot hun eigenschappen en hun methoden binnen deze enkele thread uitvoeren. Voeg een of meer secundaire threads toe om de gegevensverwerking in uw toepassing te versnellen.

Processor threads

EEN draad is een communicatiekanaal van een applicatie naar een processor. Programma's met één thread hebben communicatie nodig om in beide richtingen (van en naar de processor) te stromen tijdens de uitvoering; apps met meerdere threads kunnen verschillende kanalen openen, waardoor de uitvoering wordt versneld.

Threads & GUI

Wanneer verschillende threads in de toepassing worden uitgevoerd, rijst de vraag hoe u uw grafische gebruikersinterface kunt bijwerken als gevolg van een threaduitvoering. Het antwoord ligt in de TThread-klasse Synchroniseren methode.

Als u de gebruikersinterface of hoofdthread van uw toepassing wilt bijwerken vanaf een secundaire thread, moet u de methode Synchroniseren aanroepen. Deze techniek is een thread-veilige methode die multi-threading conflicten voorkomt die kunnen ontstaan ​​door toegang te krijgen tot objecteigenschappen of methoden die niet thread-safe zijn, of bronnen te gebruiken die niet in de hoofdthread van uitvoering staan.

Hieronder is een voorbeelddemo die verschillende knoppen met voortgangsbalken gebruikt, waarbij elke voortgangsbalk de huidige "status" van de uitvoering van de thread weergeeft.

eenheid MainU;
koppel
toepassingen
Windows, Berichten, SysUtils, Varianten, Classes, Graphics, Controls, Forms,
Dialogen, ComCtrls, StdCtrls, ExtCtrls;
type
// onderscheppingsklasse
TButton = class (StdCtrls.TButton)
OwnedThread: TThread;
ProgressBar: TProgressBar;
einde;
TMyThread = class (TThread)
privaat
FCounter: geheel getal;
FCountTo: Geheel getal;
FProgressBar: TProgressBar;
FOwnerButton: TButton;
procedure DoProgress;
procedure SetCountTo (const Waarde: geheel getal);
procedure SetProgressBar (const Waarde: TProgressBar);
procedure SetOwnerButton (const Waarde: TButton);
beschermde
procedure uitvoeren; overschrijven;
openbaar
constructor Create (CreateSuspended: Boolean);
eigenschap CountTo: Integer lees FCountTo schrijf SetCountTo;
eigenschap ProgressBar: TProgressBar lees FProgressBar schrijf SetProgressBar;
eigenschap OwnerButton: TButton lees FOwnerButton schrijf SetOwnerButton;
einde;
TMainForm = class (TForm)
Button1: TButton;
ProgressBar1: TProgressBar;
Button2: TButton;
ProgressBar2: TProgressBar;
Button3: TButton;
ProgressBar3: TProgressBar;
Button4: TButton;
ProgressBar4: TProgressBar;
Button5: TButton;
ProgressBar5: TProgressBar;
procedure Button1Click (afzender: TObject);
einde;
var
MainForm: TMainForm;
implementatie
$ R * .dfm
TMyThread
constructor TMyThread.Create (CreateSuspended: Boolean);
beginnen
geërfd;
FCounter: = 0;
FCountTo: = MAXINT;
einde;
procedure TMyThread.DoProgress;
var
PctDone: Extended;
beginnen
PctDone: = (FCounter / FCountTo);
FProgressBar.Position: = Round (FProgressBar.Step * PctDone);
FOwnerButton.Caption: = FormatFloat ('0.00%', PctDone * 100);
einde;
procedure TMyThread.Execute;
const
Interval = 1000000;
beginnen
FreeOnTerminate: = True;
FProgressBar.Max: = FCountTo div Interval;
FProgressBar.Step: = FProgressBar.Max;
terwijl FCounter < FCountTo do
beginnen
als FCounter mod Interval = 0 dan Synchroniseer (DoProgress);
Inc (FCounter);
einde;
FOwnerButton.Caption: = 'Start';
FOwnerButton.OwnedThread: = nul;
FProgressBar.Position: = FProgressBar.Max;
einde;
procedure TMyThread.SetCountTo (const Waarde: geheel getal);
beginnen
FCountTo: = Waarde;
einde;
procedure TMyThread.SetOwnerButton (const Waarde: TButton);
beginnen
FOwnerButton: = Waarde;
einde;
procedure TMyThread.SetProgressBar (const Waarde: TProgressBar);
beginnen
FProgressBar: = Waarde;
einde;
procedure TMainForm.Button1Click (afzender: TObject);
var
aButton: TButton;
aThread: TMyThread;
aProgressBar: TProgressBar;
beginnen
aButton: = TButton (afzender);
indien niet toegewezen (aButton.OwnedThread) dan
beginnen
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: = 'Pauze';
einde
anders
beginnen
als aButton.OwnedThread.Suspended dan
aButton.OwnedThread.Resume
anders
aButton.OwnedThread.Suspend;
aButton.Caption: = 'Uitvoeren';
einde;
einde;
einde.

Dank aan Jens Borrisholt voor het indienen van dit codevoorbeeld.