Reputation: 2378
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
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
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
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
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