Now let’s look at
TThread closely.
Creating a descendant of TThread.
TThread is an abstract class that means that you can’t use
TThread class itself. You need to create a subclass, which overrides
Execute method at least. Each new instance of a subclass object is a new thread of execution. Multiple instances of a TThread derived class make a Delphi application multi-threaded. Delphi documentation notes: “the recommended limit is 16 threads per process on single processor systems”.
TThread virtual and protected methods.
A protected member is visible anywhere in the module where its class is declared and from any descendant class, regardless of the module where the descendant class appears. Protected members are intended for use only in the implementation of derived classes.
A virtual method can be redefined in descendant classes, but still be called in the ancestor class. The address of a virtual method isn’t determined at compile time; instead, the object where the method is defined looks up the address at runtime.
Execute method.
Execute method is an abstract method that contains the code which executes when the thread is run.
Override Execute and insert the code that should be executed when the thread runs. Execute is responsible for checking the value of the Terminated property to determine if the thread needs to exit.
DoTerminate method.
Current implementation of
DoTerminate method generates an
OnTerminate event in the context of main VCL thread using
Synchronize.
Method is called just after
Execute returns.
DoTerminate is virtual method and can be overridden in subclass.
Terminated property.
Indicates that the thread has been asked to terminate. The
Terminate method sets the
Terminated property to True. The thread’s
Execute method and any methods that
Execute calls should check
Terminated periodically and exit when it’s True.
Program example.
type
TForm1 = class(TForm)
Memo1: TMemo;
GroupBox1: TGroupBox;
seTimeToWork: TSpinEdit;
Label1: TLabel;
btnCreate: TButton;
btnTerminate: TButton;
procedure FormShow(Sender: TObject);
procedure btnCreateClick(Sender: TObject);
procedure btnTerminateClick(Sender: TObject);
private
FThread:TThread;
procedure EnableButtons;
procedure OnTerminate(Sender:TObject);
end;
TMyThread=class(TThread)
private
FTimeToWork:integer;
protected
procedure Execute;override;
public
constructor Create(TimeToWork:integer);
end;
var
Form1: TForm1;
implementation
{$R *.DFM}
// TMyThread
constructor TMyThread.Create(TimeToWork: integer);
begin
FTimeToWork:=TimeToWork;
inherited Create(True);
end;
procedure TMyThread.Execute;
var
T:Integer;
begin
t:=FTimeToWork;
Form1.Memo1.Lines.Add('Begin execution');
while not Terminated and (t>0) do
begin
Form1.Memo1.Lines.Add(format('Remaining %5.2f%%',[t/FTimeToWork*100]));
Sleep(500);
dec(t,500);
end;
if Terminated then
Form1.Memo1.Lines.Add('Terminated by user');
Form1.Memo1.Lines.Add('Finish execution');
end;
// TForm1
procedure TForm1.EnableButtons;
begin
btnCreate.Enabled:=not Assigned(FThread);
btnTerminate.Enabled:= Assigned(FThread);
end;
procedure TForm1.FormShow(Sender: TObject);
begin
EnableButtons;
end;
procedure TForm1.btnCreateClick(Sender: TObject);
begin
FThread:=TMyThread.Create(seTimeToWork.Value);
FThread.OnTerminate:=OnTerminate;
EnableButtons;
FThread.Resume;
end;
procedure TForm1.btnTerminateClick(Sender: TObject);
begin
FThread.Terminate;
end;
procedure TForm1.OnTerminate(Sender: TObject);
begin
FThread:=nil;
EnableButtons;
end;
end.
Code explanation.
It’s very simple program. Source code contains two classes:
TForm1 class.
TForm1 class inherited from
TForm represents the main application form that owns Memo, two buttons and spin-edit controls.
In addition, there are some event handlers.
Procedure
btnCreateClick() is called when user clicks “Create” button. It creates new instance of
TMyThread object and stores pointer in
FThread field.
Pressing “Terminate” button generates
btnTerminateClick() event that calls
Terminate method of thread object signaling termination request.
Note that only one thread object instance referenced by
FThread field is created at one time. “Create” button is disabled when the thread object instance exists (
FThread<>nil) and vice versa enabled if there are no thread object (
Fthread = nil).
OnTerminate event occurs when the thread finishes its execution. It permits further calls of
btnCreateClick() by enabling “Create” button, so user can create one more thread.
TMyThread class.
TMyThread class inherited from
TThread represents new thread.
TmyThread.Execute simulates some “work” during user-determined time and outputs percentage of “work” done into memo control. This method periodically checks
Terminated property and exits when it’s True.
Note that
Terminate method of
TThread does not terminate thread immediately. It only stores true in some
TThread object private field that can be accessed through
Terminated property. Also note that
Terminated property is protected, that means that only
TThread subclasses can access it.