Search code examples
multithreadingdelphiconnectiondatasnap

Delphi: Verify DataSnap connection via TThread


We have an application in which the user can talk to us, it works fine, he create a new conversation, we chat, and that's ok. But, before start chatting, he needs to connect to the DataSnap Server, and that's where I'm trying to make a Thread. Every 5min, a timer would trigger his event to create the Thread and try to connect on the server, as below:

My Thread:

unit UThreadSnapConnection;

interface

uses
  System.Classes, System.SysUtils, Data.SqlExpr;

type
  TThreadSnapConnection = class(TThread)
  private
    FSnap: TSQLConnection;
    procedure TryToConnect;
  protected
    procedure Execute; override;
    constructor Create;
  public
    DMSnap: TSQLConnection;
    HostName: String;
    Port: String;
  end;

implementation

{ TThreadSnapConnection }

constructor TThreadSnapConnection.Create;
begin
  inherited Create(True);
  FreeOnTerminate := True;
end;

procedure TThreadSnapConnection.TryToConnect;
begin
  try
    FSnap := DMSnap.CloneConnection;
    FSnap.Connected := False;

    try
      FSnap.Connected := True;
    except

    end;

    if FSnap.Connected then
      DMSnap.Connected := True;
  finally
    FreeAndNil(FSnap);
  end;
end;

procedure TThreadSnapConnection.Execute;
begin
  Synchronize(TryToConnect);
end;

end.

My Timer:

procedure TMyDataModuleSnap.TimerSnapTimer(Sender: TObject);
var
  MyThread: TThreadSnapConnection;
begin
  if not(MySQLConnection.Connected) then
  begin
    MyThread := TThreadSnapConnection.Create;

    MyThread.DMSnap   := MySQLConnection;
    MyThread.HostName := 'localhost';
    MyThread.Port     := '211';

    MyThread.Resume;
  end;
end;

What I'm doing is an attempt to connect to the server, if it works, then it will make my data module connect.

My problem is, everytime the line

FSnap.Connected := True;

execute it freezes for 1~2 seconds the application, and the reason I made a thread was to not freeze. As long as I know, it should not bother at all the application, so I started to think maybe it's the work it does when setting the Connected property to True, which will freeze independent if it's thread or not.

Is there any way to not freeze when trying to connect?

And this is my first thread and maybe I just misunderstood things and that's not how thread works, but well, if it is not then I need to know, or at least understand what I'm doing wrong with it.

EDIT: The test I'm doing is, I start the application without starting the server, so it will try to connect unsuccessful, and my data module will not connect too.


Solution

  • There are two options:

    1. while the OnTimer event of a TTimer is executed in the thread which has created the timer, you may consider to create the instance outside the main thread
    2. you may consider to use a TThread class instance

    The following applies to the #2.

    Using a TEvent in the Execute procedure of your thread you can wait for an amount of FInterval time before the execution of the next block of code.
    When the Terminated property is set to True, this approach allows the Execute method to immediately return also during the interval count unlike the adoption of a TThread.Sleep(FInterval); call which would freeze the thread itself for the amount of time specified.

    The main thread can be optionally notified using a TNotifyEvent when done.

    TMyThread = class(TThread)
      private
        FInterval: Integer;
        FTerminateEvent: TEvent;
      protected
        procedure Execute; override;
        procedure TerminatedSet; override;
      public
        OnEndJob: TNotifyEvent;
        constructor Create(Interval: Cardinal; CreateSuspended: Boolean);
        destructor Destroy; override;
    end;
    
    constructor TMyThread.Create(Interval: Cardinal; CreateSuspended: Boolean);
    begin
      inherited Create(CreateSuspended);
      FInterval := Interval;
      FTerminateEvent := TEvent.Create(nil, False, False, '');
    end;
    
    destructor TMyThread.Destroy;
    begin
      FTerminateEvent.Free;
      inherited;
    end;
    
    procedure TMyThread.TerminatedSet;
    begin
      inherited;
      FTerminateEvent.SetEvent;
    end
    
    procedure TMyThread.Execute;
    begin
      while not Terminated do begin
        //do your stuff
    
        //notify your connection to the main thread if you want
        if Assigned(OnEndJob) then
          Synchronize(procedure
              begin
                OnEndJob(Self);
              end);
    
        //wait fo some amount of time before continue the execution
        if wrSignaled = FterminateEvent.WaitFor(FInterval) then
          Break;
      end;
    end;
    

    Don't synchonize the code you want to be executed in a thread: in Delphi a syncronized block is always executed in the calling thread.