M.mhr
M.mhr

Reputation: 1749

Delphi : How to create and use Thread locally?

My database is in a VPS and I should get some query from my tables

Because of getting query from server taking long time ( depending on Internet speed ! ) , I want to use threads to get queries

Now I create a thread and get query and then send result to my forms with sending and handling messages

I want to know is it possible to create and use a thread locally ?!?

My mean is :

procedure Requery;
var
 ...
begin
 Create Thread;
 ...

 Pass my Query Component to Thread
 ...

 Getting Query in Thread;
 ...

 Terminate and Free Thread
 ...

 Do next jobs with Query;
 ...

end;

The main part is last part ( Do next jobs ... ) , I dont want to use query result in a message handler and I want to use them in the same procedure and after thread job

Is it possible ?!

I think this is not possible with Delphi TThread class and I should use other threading techniques ...

Upvotes: 5

Views: 16558

Answers (3)

Remy Lebeau
Remy Lebeau

Reputation: 595712

What you describe is not the best use of a thread. The calling code is blocked until the thread is finished. That negates the use of running code in parallel at all. You could just perform the query directly instead:

procedure Requery;
var
  ...
begin
  ...
  // run query
  // do next jobs with query
  ...
end;

That being said, since you are using XE6, you can create a "local" thread by using the TThread.CreateAnonymousThread() method, specifying an anonymous procedure that "captures" the variables you want it to work with, eg:

procedure Requery;
var
  Event: TEvent;
  H: THandle;
begin
  Event := TEvent.Create;
  try
    TThread.CreateAnonymousThread(
      procedure
      begin
        try
          // run query in thread
        finally
          Event.SetEvent;
        end;
      end
    ).Start;
    H := Event.Handle;
    while MsgWaitForMultipleObjects(1, H, False, INFINITE, QS_ALLINPUT) = (WAIT_OBJECT_0+1) do
      Application.ProcessMessages;
  finally
    Event.Free;
  end;

  // Do next jobs with query
  ...
end;

Alternatively:

procedure Requery;
var
  Thread: TThread;
  H: THandle;
begin
  Thread := TThread.CreateAnonymousThread(
    procedure
    begin
      // run query in thread
    end
  );
  try
    Thread.FreeOnTerminate := False;
    H := Thread.Handle;
    Thread.Start;
    while MsgWaitForMultipleObjects(1, H, False, INFINITE, QS_ALLINPUT) = (WAIT_OBJECT_0+1) do
      Application.ProcessMessages;
  finally
    Thread.Free;
  end;

  // Do next jobs with query
  ...
end;

However, threading is more useful when you let it run in the background while you do other things and then you act when the thread has finished its work. For example:

procedure TMyForm.Requery;
var
  Thread: TThread;
begin
  Thread := TThread.CreateAnonymousThread(
    procedure
    begin
      // run query in thread
    end
  );
  Thread.OnTerminate := QueryFinished;
  Thread.Start;
end;

procedure TMyForm.QueryFinished(Sender: TObject);
begin
  if TThread(Sender).FatalException <> nil then
  begin
    // something went wrong
    Exit;
  end;
  // Do next jobs with query
end;

Upvotes: 14

Johan
Johan

Reputation: 76537

Yes you can do that.

The way I would do it is to add an event-handler to your form.
You'll have to link the event-handler in code, but that's not that difficult.

Create a thread like so:

TMyEventHandler = procedure(Sender: TObject) of object;

type
  TMyThread = class(TThread)
  strict private
    FDoneEvent: TMyEvent;
    FDone: boolean;
    FQuery: TFDQuery;
    constructor Create(DoneEvent: TMyEventHandler; Query: TFDQuery);
    procedure Execute; override;
    function GetQuery: TFDQuery;
  public
    property Query read GetQuery;
  end;

  TForm1 = class(TForm)
    FDQuery1: TFDQuery;  //Do not connect the FDQuery1 to anything!
    DataSource1: TDataSource;
    DBGrid1: TDBGrid;
  private
    FOnThreadDone: TMyEventHandler;
    FMyThread: TMyThread;
    procedure DoThreadDone;
    procedure ThreadDone;
  public
    property OnThreadDone: TMyEventHandler read FOnThreadDone write FOnThreadDone;
  ....

implementation

constructor TMyThread.Create(DoneEvent: TMyEvent; Query: TFDQuery);
begin
  inherited Create(true);
  FDoneEvent:= DoneEvent;
  FQuery:= Query;
  Start;
end;

procedure TMyThread.Execute;
begin
  //Do whatever with the query
  //when done do:
  FDone:= true;
  Synchonize(Form1.DoThreadDone);
end;

function TMyThread.GetQuery: TFDQuery;
begin
  if not Done then Result:= nil else Result:= FQuery;
end;

procedure TForm1.DoThreadDone;
begin
  if Assigned(FOnThreadDone) then FOnThreadDone(Self);
end;

procedure TForm1.ThreadDone(Sender: TObject);
begin
  ShowMessage('Query is done');
  //Now you can display the result of the query, by wiring it
  //to a dataset.
  MyDataSource1.Dataset:= FMyThread.Query;
  FMyThread.Free;
end;

procedure TForm1.StartTheQuery;
begin
  OnThreadDone:= Self.ThreadDone;
  FMyThread:= TMyThread.Create(OnThreadDone, FDQuery1);
end;

Now the query will run in the background and signal your event handler when it is done. Meanwhile you can do all the mousing around and user interaction you want without having to worry. Note that you cannot use FDQuery1 at all whilst the thread is using it, and you cannot have FDQuery1 wired to a DataSource whilst it's the thread is running with it.
Leave it unwired and wire it in the ThreadDone event handler as shown.

Upvotes: 0

Agustin Seifert
Agustin Seifert

Reputation: 1968

I think that using a thread this way isn't a good idea, but the answer is yes. You can do it.

procedure LocalThread;
var
  LThread: TCustomThread; //Your thread class
  LThreadResult: xxxxxxx//Your result type
begin
  LThread := TCustomThread.Create(True);
  try
    //Assign your properties

    LThread.Start;

    //Option A: blocking
    LThread.WaitFor;

    //Option B: non blocking
    while not LThread.Finished do
    begin
      Sleep(xx);
      //Some progress here ??
    end;

    //Here query your thread for your result property
    LThreadResult := LThread.MyResultProperty;
  finally
    LThread.Free;
  end

  //Do next jobs with LThreadResult
end;

Upvotes: 0

Related Questions