Fayyaz
Fayyaz

Reputation: 219

What is the difference between using TTimer and using TThread

I have developed an application that connects to SQL Server database and reads some data from tables every 1 second.
For this purpose I use TTimer but the delay of the database response affects my application performance.

I know a little about TThread in Delphi, what I want to know now is the difference between using TTimer and TThread? And using TThread instead of TTimer is useful for my application performance in this case?

Upvotes: 6

Views: 3804

Answers (5)

SzakiLaci
SzakiLaci

Reputation: 367

Whether TTimer or TThread is used, it is recommended to run a query only to get data that has been changed. To do that you need to:

  • Add a 'modified' (TIMESTAMP) column to each table
  • Add a 'deleted' (TIMESTAMP) column to each table
  • Add a trigger for INSERT OR UPDATE to update 'modified' field with CURRENT_TIMESTAMP
  • Add a DESC index on the 'modified' field to speed up queries
  • Never delete a row, only update 'deleted' field with CURRENT_TIMESTAMP

After first read, it is enough to ask for new data:

select c.* from 'customers' as c where c.modified > '2019...'

You read all data at once and store the result in a temporary memory-array.
After closed the dataset >> you compare (syncronized) with the main-array.

To update data >> run a separate SQL.

Upvotes: 0

fantaghirocco
fantaghirocco

Reputation: 4878

The main difference between the two can be found in their class definition:

  • TTimer = class(TComponent)

  • TThread = class

While the TTimer class extends TComponent and is a component itself, TThread is an abstract class which extends TObject.

TThread exposes static methods like TThread.Sleep and a peculiar protected method called Execute which must be implemented in the derived class in order to perform the desired job. TThread directly uses the Processes and Threads functions of the guest OS.


... for this purpose I use TTimer but the delay of Database response affect on my application performance

The reason why this happens is because the OnTimer event of the TTimer object is executed in the calling thread: when a TTimer component is put into a form and its OnTimer event is implemented, the code is executed in the main thread.

The TThread approach is more flexible: if for some reason the code must be performed in the main thread, this can be achieved nesting a sinchronized block inside the thread's Execute method.


If you want to execute database requests in a repeated manner after some time interval, you better consider using a TThread in combination with a TEvent object.

An example of class definition using TEvent:

TMyThread = class(TThread)
  private
    FInterval: Integer;
    FWaitEvent: TEvent;
  protected
    procedure Execute; override;
    procedure TerminatedSet; override;
  public
    constructor Create(Interval: Cardinal; CreateSuspended: Boolean);
    destructor Destroy; override;
end;

The implemented class:

constructor TMyThread.Create(Interval: Cardinal; CreateSuspended: Boolean);
begin
  inherited Create(CreateSuspended);
  FInterval := Interval;
  FWaitEvent := TEvent.Create(nil, False, False, '');
end;

destructor TMyThread.Destroy;
begin
  FWaitEvent.Free;
  inherited;
end;

procedure TMyThread.TerminatedSet;
begin
  inherited;
  FWaitEvent.SetEvent;
end;

procedure TMyThread.Execute;
begin
  inherited;
  while not Terminated do begin
    //do your stuff

    //wait fo some amount of time before continue the execution
    if wrSignaled = FWaitEvent.WaitFor(FInterval) then
      Break;
  end;
end;

The WaitFor method called on the FWaitEvent object allows to wait for the desired amount of time.

The implementation of the thread's TerminatedSet method allows to put the FWaitEvent object in a signaled state and then exit from the thread before the interval has elapsed.

Upvotes: 3

MartynA
MartynA

Reputation: 30735

This doesn't specifically address your q, but as noted in a comment to one of the other answers, polling a database at the frequency you're doing isn't a good idea, especially if other users are trying to access it.

There are various ways to get notifications from database servers when data changes, without needing to continually poll them. This Embarcadero paper has a very useful review of what's available for various DBMSs:

http://docwiki.embarcadero.com/RADStudio/XE8/en/Database_Alerts_%28FireDAC%29

If your Delphi version includes FireDAC, as you'll see from the link that you could use TFDEventAlerter to receive notifications of data changes on the server if your DBMS supports it.

If you're using Interbase or Firebird (and maybe some others), there are alternate Delphi components available that don't require FireDAC, e.g. TIBEventAlerter in the IBExpress ibrary for Interbase.

Upvotes: 3

Peter-John Jansen
Peter-John Jansen

Reputation: 643

I would suggest keeping your TTimer if you want to on your main form

Then inside your TTimer create a TTask

http://docwiki.embarcadero.com/RADStudio/XE8/en/Tutorial:_Using_Tasks_from_the_Parallel_Programming_Library

https://delphiaball.co.uk/2014/09/08/using-ttask-from-the-parallel-programming-library/

And doing all your DB work in there, but as others have suggested checking every 1 second is not very good practice.

Something like this :

Global var downloaddata : ITask

TimerTimer.Timer(Sender: TObject);
begin
    if downloaddata.Status = TTaskStatus.Running then
    begin
    //If it is already running don't start it again
        Exit;
    end;

    downloaddata := TTask.Create (procedure ()
    var //Create Thread var here
        MyConnection : TFDConnection;
        MyQuery : TFDQuery;
    begin
        //Do all your Query logic in here

        //If you need to do any UI related modifications
         TThread.Synchronize(TThread.CurrentThread,procedure()
            begin 
                //Remeber to wrap them inside a Syncronize
            end);
     //If you have Query variables and this is running on mobile with ARC 
     //remember to set their connection :  properties to nil to avoid memory leaks
     //http:stackoverflow.com/questions/32010583/what-happens-to-database-connection-objectsmydac-tmyconnection-under-arc
    MyQuery.connection := nil
end);
downloaddata.start

There are much better solutions available this is just a quick basic answer but it should guide you into something better.

Doing logic in your thread would keep your UI repsonsive, but beware that TThread.Syncronize will wait for the main form and depending on the situation TThread.queue would be a better call.

Upvotes: 2

Remy Lebeau
Remy Lebeau

Reputation: 598134

TTimer is a message-based timer. It posts WM_TIMER messages to the message queue of the thread that creates it. Your database operations are blocking that thread from processing new messages in a timely manner. Assuming your TTimer is in the main UI thread, that is why your app performance suffers. Moving the database operations into a worker thread prevents the main thread's message loop from being blocked.

Upvotes: 6

Related Questions