Multi Threading in Delphi
Multi Threading in Delphi
Delphi! is my art of programming...This blog contains technical solutions and tips for
Delphi programming. I am just trying to share knowledges that I gained from others and
which will help others. (Delphi Tokyo/XE7/ XE6 / XE5 / XE4 / XE3 / XE2 / XE,
FireMonkey, FireDAC, DataSnap, QuickReport, DevExpress, Woll2Woll, TMS
Components, Indy, REMObjects SDK....)
SUBSCRIBE
So first lets go about Process, Process is a program that runs on a system and uses system
resources like CPU, Memory etc. And every process has a main thread. In a Process many
actions can be performed one by one on fast in fast perform order. And Thread is generally
used to perform several set of actions at once in situations like some actions may cause a
considerable delay but during that period the program should be able to perform other actions
too. For an example in windows explorer we are copying a large volume of data from one
folder to another folder and we found it will take long time but during copy we can do other
work also. So in this case copy process is going on in a new thread which is not effecting to
main thread.
Threads are mostly used in case of performance related problems. Here are some examples
where we might use threads.
- Doing lengthy processing : When a windows application is calculating it cannot process any
more messages. As a result, the display cannot be updated.
- Doing background processing : Some tasks may not be time critical, but need to execute
continuously.
- Doing I/O work : I/O to disk or to network can have unpredictable delays. Threads allow you
to ensure that I/O operation should not delay unrelated parts of your application.
Following image shows how VCL main thread executes during application run.
To create and execute a separate Thread in application, Delphi provides TThread class which
is an abstract class. It provides options for creating threads, executing threads and terminate
when required. How ever we cannot create TThread class objects directly as it is an abstract
class. So we have to create a new class by inheriting from this class and need to implement
Execute method. Then we can create the object of this class to create a new thread. Then we
can start a thread to perform some other actions. And a Thread will terminate automatically or
we can terminate manually as per required.
Lets see an example of thread which find list of prime numbers till give number and save into a
file...
type
TP i Th d l (TTh d)
TPrimeThrd = class(TThread)
private
FToNumber: integer;
protected
public
end;
.......
var
iCnt: integer;
begin
result := true;
begin
result := false;
exit;
end;
exit;
begin
begin
result := false;
exit;
end;
end;
end;
procedure TPrimeThrd.Execute;
var
iCnt: integer;
slPrime: TStringlist;
begin
Try
slPrime := TStringlist.Create(Nil);
slPrime.Clear;
begin
begin
slPrime.Add(inttostr(iCnt));
end;
end;
slPrime.SavetoFile('C:\Prime1.txt');
finally
slPrime.Free;
end;
end;
On above code TPrimeThrd class inheriting from TThread class. ToNumber is property used to
pass a number till that we will find the prime numbers. IsPrime method to check a number is
prime or not. We have to override Execute method as TThread class is abstract class. And
Execute method contains actual codes that will execute when a Thread starts.
Lets see how to use above TPrimeThrd objects to start a new Thread...
type
TPrimeFrm = class(TForm)
NumEdit: TEdit;
SpawnButton: TButton;
private
{ Private declarations }
public
{ Public declarations }
end;
......
var
NewThread: TPrimeThrd;
begin
begin
NewThread := TPrimeThrd.Create(True);
NewThread.FreeOnTerminate := True;
try
NewThread.ToNumber := StrToInt(NumEdit.Text);
NewThread.Resume;
except on EConvertError do
begin
NewThread.Free;
end;
end;
end;
On above code each time the "Spawn" button is clicked, the program creates a new
NewThread as TPrimeThrd object. We have passed True as parameter to TPrimeThrd.Create,
this means thread object will be created as suspended and thread will not start till we call
Resume or Start method. If we pass False means Thread will be created and will start
immediately. FreeonTerminate defines that how Thread object will free. If FreeeonTerminate is
True then Thread object will free automatically once executed successfully means after
statements written in Execute method runs successfully. And on above code after create
thread object we have we have assigned ToNumber property so that thread will find prime
numbers from 1 to till that number. And Resume method will start a suspended thread. So
once Resume method called Thread object will start and TPrimeThrd.Execute will be called.
And if thread runs successfully it will free thread object automatically as
FreeOnTerminate=True. If any error occurred then NewThread.Free will free the thread
object.
Sometime we have more than one thread and we need to communicate between them to
access shared resources. Suppose two threads accessing a shared integer variable can result
in complete disaster, and un-synchronized access to shared resources or VCL calls will result
in many hours of fraught debugging. So we have to protect all the shared resources with
secure communication. if we do not have adequate synchronization then we may not able to
do followings...
- Accessing any form of shared resource between two threads.
- Playing with thread unsafe parts of the VCL in a non-VCL thread.
- Attempting to do graphics operations in a separate thread.
For this issue Delphi provides Synchronize method which takes as a parameter a parameter
less method which you want to be executed. You are then guaranteed that the code in the
parameter less method will be executed as a result of the synchronize call, and will not conflict
with the VCL thread. Code which is invoked when synchronize is called can perform anything
that the main VCL thread might do and can also modify data associated with its own thread
object
For example We will modify our prime number program, so that instead of showing a message
box, it indicates whether a number is prime or not by adding some text to a memo in the main
form. First of all, we'll add a new memo (ResultsMemo) to our main form.
type
TPrimeFrm = class(TForm)
NumEdit: TEdit;
SpawnButton: TButton;
ResultsMemo: TMemo;
private
{ Private declarations }
public
{ Public declarations }
end;
We add another method (UpdateResults) to our thread which displays the results on the memo
instead of storing in a TStringlist and then saving into a file. We call Synchronize, passing this
method as a parameter. Here UpdateResults accesses both the main form and a result string.
type
TPrimeThrd = class(TThread)
private
FToNumber: integer;
FResultString: string;
protected
procedure UpdateResults;
public
end;
........
procedure TPrimeThrd.UpdateResults;
begin
PrimeFrm.ResultsMemo.Lines.Add(FResultString);
end;
........
procedure TPrimeThrd.Execute;
var
iCnt: integer;
begin
begin
if IsPrime(iCnt) then
begin
FResultString := inttostr(iCnt);
Synchronize(UpdateResults);
end;
end;
end;
TImport = class(TThread)
private
CDImportData : TClientDataSet;
connMain : TAdoConnection;
qryImport : TADOQuery;
iImportRecord,iImpRecCount : Integer;
procedure ImportData;
procedure UpdateImportUI;
procedure UISettingsEnable;
procedure UISettingsDisable;
public
procedure Execute;Override;
end;
........
procedure TImport.Execute;
begin
inherited;
CoInitialize(nil);
ImportData;
end;
procedure TImport.ImportData;
var
bIsRecExist : Boolean;
begin
try
iImportRecord := 0;
iImpRecCount := 0;
bIsRecExist := False;
connMain := TAdoConnection.Create(Nil);
connMain.LoginPrompt := False;
Source=yoursource;Extended Properties=""';
connMain.Open;
qryImport := TADOQuery.Create(nil);
qryImport.Close;
qryImport.Connection := connMain;
qryImport.Open;
CDImportData := TClientDataSet.Create(nil);
CDImportData.FieldDefs.Add('Id',ftString,10,True);
CDImportData.FieldDefs.Add('FIRST_NAME',ftString,50,True);
CDImportData.FieldDefs.Add('EMAIL',ftString,50,False);
CDImportData.CreateDataSet;
CDImportData.Active := True;
CDImportData.Open;
begin
bIsRecExist := True;
iImpRecCount := qryImport.RecordCount;
Synchronize(UISettingsEnable);
qryImport.First;
begin
CDImportData.Append;
CDImportData.FieldByName('Id').AsString := qryImport.FieldByName('Id').AsString;
CDImportData.FieldByName('FIRST_NAME').AsString := qryImport.FieldByName('FIRST_NAME').AsString;
CDImportData.FieldByName('EMAIL').AsString := qryImport.FieldByName('EMAIL').AsString;
CDImportData.Post;
qryImport.Next;
iImportRecord := iImportRecord + 1;
Synchronize(UpdateImportUI);
end;
end;
CDImportData.SaveToFile('C:\MYIMPORT.CDS');
finally
if bIsRecExist then
Synchronize(UISettingsDisable);
qryImport.Close;
FreeAndNil(qryImport)
connMain.Close;
FreeAndNil(connMain)
CDImportData.Close;
FreeAndNil(CDImportData)
end;
end;
procedure TImport.UISettingsDisable;
begin
frmMain.lblIndicate.Visible := False;
frmMain.pbar.Visible := False;
frmMain.btnImport.Enabled := True;
end;
procedure TImport.UISettingsEnable;
begin
frmMain.lblIndicate.Visible := True;
frmMain.pbar.Visible := True;
frmMain.btnImport.Enabled := False;
frmMain.pbar.Max := iImpRecCount;
end;
procedure TImport.UpdateImportUI;
begin
frmMain.pbar.Position := iImportRecord;
end;
type
TfrmMain = class(TForm)
btnImport: TButton;
pbar: TProgressBar;
lblIndicate: TLabel;
private
{ Private declarations }
public
{ Public declarations }
end;
..........
var
dImport : TImport;
begin
dImport := TImport.Create(True);
dImport.FreeOnTerminate := True;
dImport.Resume;
end;
ShellExecute in Delphi
- July 15, 2014
ShellExecute in Delphi – Launch external applications.ShellExecute is Delphi Windows API
function that is mostly used for launch external applications from our Delphi application. This
function is linked to the ShellExecute Windows API function. The function returns an integer …
READ MORE
In this blog I will describe how to read and write data from and to an Excel file. Sometime in
our application we use Excel for reporting purpose, for data import / export purpose and for
other works. So here I will explain how to access an Excel file and use for data read / write. …
READ MORE
Sometime we need some special directories path from Microsoft Windows system to store
User data or to copy some files etc. So we can get those folder paths in Delphi in several ways.
In this blog I have tried to cover all the ways. If I have left something please feel free to add …
READ MORE
Powered by Blogger
JITENDRA GOUDA
VISIT PROFILE
Archive
Labels
Useful Sites
Embercadero Community
Embercadero Academy
Learn Delphi in 21 Days
Delphi Basics
Delphi Wikia
Delphi Wikia Videos
Delphi TV Videos
Delphi Programming by Zarko
Stackoverflow Delphi
Delphi FAQ
The Delphi Corner
Delphi Pages
JEDI Projects
Torry's Delphi Pages
Followers
Seguidores (12)
Seguir
Total Pageviews
411,784