Download as pdf or txt
Download as pdf or txt
You are on page 1of 13

Delphi Programming Diary - Jitendra Kumar

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

Multi Threading in Delphi


- February 17, 2017

What are threads? and why we use them?


Earlier days of programming all programs were single threaded and single tasking in which
your program will ran exclusively on the machine or not at all. With increasingly sophisticated
applications and increasing demands on personal computers, now multiprocessing and
multithreading  operating systems are available. Multithreading on computer programming
was mainly required for better performance and usability.

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.

Creating a Thread in Delphi


Creating a Thread in Delphi
Before creating a separate Thread, lets get some idea about VCL main thread in Delphi
application. Actually every Delphi application has main thread  which executes when the
application starts and terminates when application exits. And during application run when we
don't do anything then main thread  suspends for some time and when user again do some
action main thread resumes and starts executing the actions.

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

    function IsPrime(iNum: integer): boolean;

    procedure Execute; override;

  public

    property ToNumber: integer write FToNumber;

  end;

.......

function TPrimeThrd.IsPrime(iNum: integer): boolean;

var

  iCnt: integer;

begin

  result := true;

  if iNum < 0 then

  begin

    result := false;

    exit;

  end;

  if iNum <= 2 then

    exit;

  for iCnt := 2 to iNum - 1 do

  begin

    if (iNum mod iCnt) = 0 then

    begin

      result := false;

      exit;

    end;

  end;

end;

procedure TPrimeThrd.Execute;

var

  iCnt: integer;

  slPrime: TStringlist;

begin
  Try

    slPrime := TStringlist.Create(Nil);

slPrime.Clear;

for iCnt :=  0 to FToNumber-1 do

begin

 if IsPrime(iCnt) then

 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;

    procedure SpawnButtonClick(Sender: TObject);

  private

      { Private declarations }

  public

      { Public declarations }

  end;

......

procedure TPrimeFrm.SpawnButtonClick(Sender: TObject);

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;

      ShowMessage('That is not a valid number!');

    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.

Following Image explains how multi thread works


Delphi Threads Communication and Synchronization 
TThread.Synchronize

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;

    procedure SpawnButtonClick(Sender: TObject);

  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

    function IsPrime: boolean;

    procedure UpdateResults;

    procedure Execute; override;

  public

    property ToNumber: integer write FToNumber;

  end;

........

procedure TPrimeThrd.UpdateResults;

begin

  PrimeFrm.ResultsMemo.Lines.Add(FResultString);

end;

........

procedure TPrimeThrd.Execute;

var

  iCnt: integer;

begin

  for iCnt :=  0 to FToNumber-1 do

  begin

    if IsPrime(iCnt) then

begin

 FResultString := inttostr(iCnt);

 Synchronize(UpdateResults);

end;

  end;

end;

Fetching data from a database by a Thread in Delphi


When we required to fetch data from a database like import or export functionality we can use
threads for better solution. In this case each threads
should have their own data components like Connection, Query etc. We should create all
Components and set required properties like Conenctionstring,  SQL at run time. We should
Open and close connections at run time only.  For example I will execute a thread which will
import some data from CONTACT table from a Oracle DB for Import. This example will also
update user number of records imported.
Import Thread...
type

  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;

connMain.ConnectionString := 'Provider=OraOLEDB.Oracle.1;Password=pass;Persist Security Info=True;User ID=user;Data

Source=yoursource;Extended Properties=""';

    connMain.Open;

qryImport := TADOQuery.Create(nil);

    qryImport.Close;

    qryImport.Connection := connMain;

    qryImport.SQL.Text := ' SELECT C.ID,C.FIRST_NAME,C.EMAIL '+

                          ' FROM CONTACT C '+

                          ' WHERE C.FIRST_NAME IS NOT NULL ';

    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;

    if qryImport.RecordCount > 0 then

    begin

      bIsRecExist := True;

      iImpRecCount := qryImport.RecordCount;

      Synchronize(UISettingsEnable);

      qryImport.First;

      while not qryImport.Eof do

      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;

    if CDImportData.RecordCount > 0 then

      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.lblIndicate.Caption := 'Importing Data ...   ' +IntToStr(iImportRecord)+ '  Out Of  '+IntToStr(iImpRecCount);

  frmMain.pbar.Position :=  iImportRecord;

end;

Using of above thread to import data in a form having recuired controls...

type

  TfrmMain = class(TForm)

    btnImport: TButton;
    pbar: TProgressBar;

    lblIndicate: TLabel;

    procedure btnImportClick(Sender: TObject);

  private

    { Private declarations }

  public

    { Public declarations }

  end;

..........

procedure TfrmMain.btnImportClick(Sender: TObject);

var

  dImport : TImport;

begin

  dImport := TImport.Create(True);

  dImport.FreeOnTerminate := True;

  dImport.Resume;

end;

Execute FreeonTerminate Resume Suspend Synchronize

Synchronize in Delphi Thread Synchronization Threads in Dlephi TThread

Enter your comment...

Popular posts from this blog

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

MS Excel Automation in Delphi


- February 02, 2018

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

How to get WINDOWS special directories path in Delphi?


- August 03, 2017

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

Theme images by Michael Elkan

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

You might also like