Synchronizing Threads and GUI in Delphi application

Synchronizing Threads and GUI

 
 
Code submitted by Jens Borrisholt

Multi-threading in Delphi lets you create applications that include several simultaneous paths of execution.

A "normal" Delphi application is single-threaded: all (VCL) objects access their properties and execute their methods within this single thread.

Threads and GUI

To speed up data processing in your application you can decide to include one or more "secondary" threads.

When several threads are running in the application a question arises: how to update your GUI (user interface) as a result of a thread execution. The question lies in the TThread class Synchronize method.

To update your application's user interface - main thread - from a secondary thread you need to call the Synchronize method - a thread-safe method that avoids multi-threading conflicts that can arise from accessing object properties or methods that are not thread-safe, or using resources not in the main thread of execution.

An example demo uses several buttons with progress bar, each progress bar displaying the current "state" of the thread execution...

Download source code

unit MainU;

 

 interface

 

 uses

   Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,

   Dialogs, ComCtrls, StdCtrls, ExtCtrls;

 

 type

   //interceptor class

   TButton = class(StdCtrls.TButton)

     OwnedThread: TThread;

     ProgressBar: TProgressBar;

   end;

 

   TMyThread = class(TThread)

   private

     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) ;

   protected

     procedure Execute; override;

   public

     constructor Create(CreateSuspended: Boolean) ;

     property CountTo: Integer read FCountTo write SetCountTo;

     property ProgressBar: TProgressBar read FProgressBar write SetProgressBar;

     property OwnerButton: TButton read FOwnerButton write SetOwnerButton;

   end;

 

   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(Sender: TObject) ;

   end;

 

 var

   MainForm: TMainForm;

 

 implementation

 

 {$R *.dfm}

 

 { TMyThread }

 

 constructor TMyThread.Create(CreateSuspended: Boolean) ;

 begin

   inherited;

   FCounter := 0;

   FCountTo := MAXINT;

 end;

 

 procedure TMyThread.DoProgress;

 var

   PctDone: Extended;

 begin

   PctDone := (FCounter / FCountTo) ;

   FProgressBar.Position := Round(FProgressBar.Step * PctDone) ;

   FOwnerButton.Caption := FormatFloat('0.00 %', PctDone * 100) ;

 end;

 

 procedure TMyThread.Execute;

 const

   Interval = 1000000;

 begin

   FreeOnTerminate := True;

   FProgressBar.Max := FCountTo div Interval;

   FProgressBar.Step := FProgressBar.Max;

 

   while FCounter < FCountTo do

   begin

     if FCounter mod Interval = 0 then Synchronize(DoProgress) ;

 

     Inc(FCounter) ;

   end;

 

   FOwnerButton.Caption := 'Start';

   FOwnerButton.OwnedThread := nil;

   FProgressBar.Position := FProgressBar.Max;

 end;

 

 procedure TMyThread.SetCountTo(const Value: Integer) ;

 begin

   FCountTo := Value;

 end;

 

 procedure TMyThread.SetOwnerButton(const Value: TButton) ;

 begin

   FOwnerButton := Value;

 end;

 

 procedure TMyThread.SetProgressBar(const Value: TProgressBar) ;

 begin

   FProgressBar := Value;

 end;

 

 procedure TMainForm.Button1Click(Sender: TObject) ;

 var

   aButton: TButton;

   aThread: TMyThread;

   aProgressBar: TProgressBar;

 begin

   aButton := TButton(Sender) ;

   if not Assigned(aButton.OwnedThread) then

   begin

     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';

   end

   else

   begin

     if aButton.OwnedThread.Suspended then

       aButton.OwnedThread.Resume

     else

       aButton.OwnedThread.Suspend;

 

     aButton.Caption := 'Run';

   end;

 end;

 

 end.

 

 

你可能感兴趣的:(application)