Monday, November 16, 2009

Go go Multi-Threading

For those that don't know what threads are, here's your chance to understand.
A thread is like an application running in your application, each thread is a new application. If you create a new VCL Application in Delphi and hit F9 to run it boom you've created a thread.
The idea of thread is that you can accomplish multiple tasks in same time, at this time most PC's have processors with 2 or more core, this is a good thing, because a processor can handle a limited number of threads until it goes crazy and user needs to restart hes PC(it happened to me few times...).
So bottom line, why do I need threads? well think of your torrent client, could it handle 2 or more downloads in same time without threads? well it could but it would be very slow because an alternative to threads would be a list of tasks that needs to be done and a loop that will execute operation, but that would freeze your application and your brain trying to solve bugs.
Now that we know something about threads, here's how you can create your custom thread that does a custom task, first you need to create a new class which inherits from TThread defined in Classes.pas.
type TCustomThread = class(TThread)
     private
            FMin, FMax: Integer;
            Position : integer;
            ProgressBar : TProgressBar;
     private
            procedure UpdateProgress;
            procedure SetProgressBarOptions;
     public
           procedure Execute; override;
     public
           constructor Create(withProgressBar: TProgressBar);
           destructor Destroy; override;
end;
and then implement it's methods
{ TCustomThread }

constructor TCustomThread.Create(withProgressBar: TProgressBar);
begin
     FMin := 0;
     FMax := 30000;
     // assign the progress bar
     ProgressBar := withProgressBar;
     // let thread free itself
     FreeOnTerminate := True;
     // do not create suspended, let it go as soon as it is created
     inherited Create(False);
end;

destructor TCustomThread.Destroy;
begin
     ProgressBar.Position := 0;
     inherited;
end;

procedure TCustomThread.Execute;
var i : Integer;
begin
     // set progressbar options
     Synchronize(SetProgressBarOptions);
     for i := FMin to FMax do begin
         // check if Self(thread) is terminated, if so exit
         if Terminated then
            Exit;
         Position := i;
         // call Synchronize to make sure you won't get errors
         // from VCL's
         Synchronize(UpdateProgress);
     end;// for i := ProgressBar.Min to ProgressBar.Max do begin
end;

procedure TCustomThread.UpdateProgress;
begin
     // force application to repaint itself and handle messages
     Application.ProcessMessages;
     // update progress bar position
     ProgressBar.Position := Position;
end;

procedure TCustomThread.SetProgressBarOptions;
begin
     ProgressBar.Min := FMin;
     ProgressBar.Max := FMax;
end;
Let's understand what TCustomThread does, it gets created using a progressbar as parameter, I've overwritten it's Execute procedure(this is the procedure which gets executed), it executes a for loop which calls UpdateProgress with Synchronize procedure which won't raise an exception if the VCL is not thread safe.
A demo application can be downloaded from this link(only source code).

3 comments:

  1. Your code is not thread safe.

    ReplyDelete
  2. interface

    type
    TCustomThread = class(TThread)
    private
    FMin, FMax: Integer;
    Position: Integer;
    ProgressBar: TProgressBar;
    private
    procedure SetProgressBarOptions;
    procedure UpdateProgress;
    public
    procedure Execute; override;
    public
    constructor Create(withProgressBar: TProgressBar);
    destructor Destroy; override;
    end;

    implementation

    { TCustomThread }

    constructor TCustomThread.Create(withProgressBar: TProgressBar);
    begin
    FMin := 0;
    FMax := 30000;
    // assign the progress bar
    ProgressBar := withProgressBar;
    // let thread free itself
    FreeOnTerminate := True;
    // do not create suspended, let it go as soon as it is created
    inherited Create(False);
    end;

    destructor TCustomThread.Destroy;
    begin
    ProgressBar.Position := 0;
    inherited;
    end;

    procedure TCustomThread.Execute;
    var
    i: Integer;
    begin
    // set progressbar options
    Synchronize(SetProgressBarOptions);
    for i := FMin to FMax do
    begin
    // check if Self(thread) is terminated, if so exit
    if Terminated then
    Exit;
    Position := i;
    // call Synchronize to make sure you won't get errors
    // from VCL's
    Synchronize(UpdateProgress);
    end;
    end;

    procedure TCustomThread.SetProgressBarOptions;
    begin
    ProgressBar.Min := FMin;
    ProgressBar.Max := FMax;
    end;

    procedure TCustomThread.UpdateProgress;
    begin
    // force application to repaint itself and handle messages
    Application.ProcessMessages;
    // update progress bar position
    ProgressBar.Position := Position;
    end;

    ReplyDelete
  3. Oh, my bad! forgot to sync the progress bar min and max and the loop start and end point. I've modified the code.
    Thank you!

    ReplyDelete

Blogroll(General programming and Delphi feeds)