Pieter van Wyk
Pieter van Wyk

Reputation: 2378

How can two different TThread descendants share an OnTerminate handler?

I have an application that launches a number threads to do stuff. I have a TThreadOnTerminate procedure for each of the threads that accesses some thread variables to populate a grid with the results. (Each thread performs a different task but the answer is always the same, ie. Success or Fail with a StringList of messages.)

So I have:

procedure TFormMain.Thread1OnTerminate(Sender: TObject);
begin
  Result := TThread1(Sender).Result;
  AddMessagesToGrid(TThread1(Sender).Messages);
end;

procedure TFormMain.Thread2OnTerminate(Sender: TObject);
begin
  Result := TThread2(Sender).Result;
  AddMessagesToGrid(TThread2(Sender).Messages);
end;

My question is the following. Can I have a 'common' OnTerminate procedure to handle the results of all my threads, as below?

procedure TFormMain.Thread1OnTerminate(Sender: TObject);
begin
  Result := <Sender Thread>.Result;
  AddMessagesToGrid(<Sender Thread>.Messages);
end;

We currently use Delphi 2007. Soon to be upgraded (I hope) to Delphi XE.

Upvotes: 3

Views: 787

Answers (4)

Ken White
Ken White

Reputation: 125651

Sure. Add a new integer field to your TThread, assign it a numeric value, and then use that in the OnTerminate:

type
  TYourThread=class(Thread)
  private
    FTag: Integer;
  public
    // Usual constructor, etc.
  published
    property Tag: Integer read FTag write FTag;
  end;

In the code that creates the thread, set the tag value:

MyThread := TYourThread.Create(True);
MyThread.Tag := 1;
MyThread.OnTerminate := ThreadTerminate;
MyThread.Resume;

Updated: Recent versions of Delphi allow threads to be named, and also use MyThread.Start rather than MyThread.Resume.)

In the OnTerminate event hander:

procedure TFormMain.ThreadTerminate(Sender: TObject);
var
  TheThread: TMyThread;
begin
  TheThread := TMyThread(Sender);
  AddMessagesToGrid(TheThread);
end;

In the AddMessagesToGrid method (or OnTerminate for the thread, also), you can tell which thread you've got:

procedure TFormMain.AddMessagesToGrid(const Thread: TMyThread);
begin
  WhatEverGrid.Cells[0, Thread.Tag] := Format('Thread %d', [Thread.Tag]);
  // Do whatever with the Thread message stringlist.
end;

(BTW, your sample OnTerminate won't work as is, as a procedure doesn't have a Result. I suspect you want to set a different Result or something...)

Upvotes: 1

RBA
RBA

Reputation: 12584

You can adapt the code to use InterlockedIncrement and InterlockedDecrement methods to know how many threads are. When the counter show that are no more threads, you can process your data.

Also, I saw that you have wrote a function for each thread. You can create a custom method and assign it to each thread

 procedure OnTerminateThrd(Sender: TObject);  

//in the create section for your thread
   yournewthread.OnTerminate := OnTerminateThrd;

in this way all your threads are using the same routine when the OnTerminate event is raised.

best regards,
Radu

Upvotes: 0

Bulan
Bulan

Reputation: 713

When you create your thread, set the OnTerminate method of the Thread to a procedure in the main form and then with the Sender object typecast it to to correct class and get the correct properties from that, like so:

procedure TFormMain.ThreadOnTerminate(Sender: TObject);
begin
  Result := (Sender as TMyBaseThreadClass).Result;
  AddMessagesToGrid((Sender as TMyBaseThreadClass).Messages);
end;

Upvotes: 0

Marjan Venema
Marjan Venema

Reputation: 19346

Yes, no reason why you couldn't, provided all threads implement the same interface, to return the Result value and the messages.

The easiest way to do that is to make an base thread class with the result and messages and make all your current threads descendants of that base class.

type
  TBaseThread = class(TThread)
  protected
    function GetResult: Integer; {virtual if you want to}
    function GetMessages: TStrings; {virtual if you want to}
  public
    property Result: Integer read GetResult;
    property Messages: TStrings read GetMessages;

and then use it as follows:

procedure TFormMain.Thread1OnTerminate(Sender: TObject);
begin
  Assert(Sender is TBaseThread);
  Result := TBaseThread(Sender).Result;
  AddMessagesToGrid(TBaseThread(Sender).Messages);
end;

Upvotes: 9

Related Questions