views:

269

answers:

3

Hi, I am new with this stuff of Threading in Delphi. so, I am trying to make a simple query aplication that make a bit call up for the database and take a bit of time, so I want to alert the user that there is a background process and have to be patient.

I tried many samples, but none of them work for me, Please, could somebody show me a simple sample that could work?

I know that I have to Declare a Type of TThread, with Create and Override Execute... etc.. but since that I am lost...

Using Delphi 7, SQL Server 2005 and ADO, Windows XP sp3.-

Thanks.

+2  A: 

Here is a sample I have found useful: Multi-Threading Delphi Database Queries

M Schenkel
Thanks for the Sample.. I will read it and I hope I could use it as a base for my learning of threads.
Jose M. Vilomar
+5  A: 

Yup, you declare a new type which inherits from TThread:

TMyWorkerThread = class(TThread)
end;

Then you add a function override for Execute():

TMyWorkerThread = class(TThread)
public
  procedure Execute; override;
end;

That procedure will be called when you start your thread. It will be executed in parallel with your main program. Let's write it.

procedure TMyWorkerThread.Execute;
begin
  //Here we do work
   DoSomeWork();
   DoMoreWork();
  //When we exit the procedure, the thread ends.
  //So we don't exit until we're done.
end;

How to use this? Let's say you want to start doing work when the user clicks button. You write an OnClick handler:

procedure TMainForm.Button1Click(Sender: TObject);
begin
  TMyWorkerThread.Create(false);
end;

That's it. After the user clicks button, your thread starts and proceeds with doing whatever it is that you wrote in Execute. If the user clicks the button again, another thread will start, and then another - one every click. They will all run in parallel, each doing all what's written in Execute() and then ending.

Let's say you want to check if the work is over. For that, you'll have to store the reference to your thread somewhere:

TMainForm = class(TForm)
{...skipped...}
public
  MyWorkerThread: TThread;
end;

procedure TMainForm.Button1Click(Sender: TObject);
begin
  //This time we make sure only one thread can be started.
  //If one thread have been started already, we don't start another.
  if MyWorkerThread<>nil then
    raise Exception.Create('One thread have already been started!');
  MyWorkerThread := TMyWorkerThread.Create(false);
end;

procedure TMainForm.Button2Click(Sender: TObject);
begin
  //If the work is not over yet, we display message informing the user we're still working
  if (MyWorkerThread<>nil) and (WaitForSingleObject(MyWorkerThread.Handle, 0)<>WAIT_OBJECT_0) then
    MessageBox(Self.Handle, pchar("The work is not yet done!"), pchar("Still running"), MB_OK);
end;

As you see, we're checking if a thread is still running by calling a Windows function called WaitForSingleObject. This function waits until the thread is done working, or the timeout is elapsed, and as we specify the timeout of 0, it just exists immediately if the thread is not over yet.

himself
I know this just example code, but a comment stating what happens if the user clicks the button more than once would be useful.
Leonardo Herrera
+3  A: 

Hello.
You can find many examples on the web of threads. The only special feature, if you are using ADO connections inside the Thread, is that you can't share the same connection.
Each thread must create its own connection, otherwise they are equal (should follow the same rules as any other thread.)

An sample that I have used is this:

  TADOSQLThread = class(TThread)
  private
    FADOQ: TADOQuery;  // Internal query
    FSQL: string;      // SQL To execute
    FID: integer;      // Internal ID

  public
    constructor Create(CreateSuspended:Boolean; AConnString:String;
                       ASQL:string; IDThread:integer);
    destructor Destroy; override;
    procedure Execute(); override;

    property ID:integer read FID write FID;
    property SQL:string read FSQL write FSQL;
    property ADOQ:TADOQuery read FADOQ write FADOQ;
  end;

The Create constructor is overrided, and look like this:

constructor TADOSQLThread.Create(CreateSuspended:Boolean; AConnString:String;
                                 ASQL:string; IDThread:integer);
begin

  inherited Create(CreateSuspended);

  // ini
  Self.FreeOnTerminate := False;

  // Create the Query
  FADOQ := TAdoquery.Create(nil);
  // assign connections
  FADOQ.ConnectionString := AConnString;
  FADOQ.SQL.Add(ASQL);
  Self.FID := IDThread;
  Self.FSQL:= ASQL;
end;

And the execute methos is very simply:

procedure TADOSQLThread.Execute();
begin

  inherited;

  try
    // Ejecutar la consulta
    Self.FADOQ.Open;
  except
    // Error al ejecutar
    ...Error treattement
  end;
end;

To start and create a thread you can use code similar to this:

  //crear el Thread
  th := TADOSQLThread.Create(True, mmConnection.Lines.Text, ASQL, AId);
  // internal for me (for controled the number of active threads and limete it)
  inc(numThreads);
  // evento finalizacion
  th.OnTerminate := TerminateThread;
  // Ejecutarlo
  th.Resume;

I have create a TerminateThread method that receive the control of threads when they finish. The only different to other threads is the connection problem. You must create a new connection on every thread, It can't shere the same ADOConnections with others.
I hope this example will be useful for you.,

Regards

Neftalí
Hi... Thanks Neftali, great sample.. I will make a try to it..
Jose M. Vilomar