Using The Thread Pool in Delphi 7
Using The Thread Pool in Delphi 7
html
Thread Pool
Hi;
I use a thread pool (with max of thread) and I would like to know :
Delphi Developer 1- how to recycle in Delphi 7 a thread once that it finished its activity.
2- how realize the wait/notify functions in Delphi 7
Regards;
Re:Thread Pool
Quote
> Hi;
> I use a thread pool (with max of thread) and I would like to know :
> 1- how to recycle in Delphi 7 a thread once that it finished its activity.
> 2- how realize the wait/notify functions in Delphi 7
> Regards;
Re:Thread Pool
Delphi Developer
1 of 10 25/10/2021 17:14
Thread Pool - delphi http://www.delphigroups.info/2/0e/407164.html
Quote
You have to make sure the thread does not fall out of its Execute method
when it has done its task. If it falls out of Execute the API thread behind
the TThread object is destroyed and cannot be restarted. Typically the
Execute method of a poolable thread looks something like
while not Terminated do begin
try
WaitForMoreWorkOrDeathSignal;
while not Terminated do begin
PerformWork;
AddToPool(self);
end;
except
on E: Exception do begin
LogException(E);
If CanRecover then
AddToPool(self);
end;
end;
end;
Some outside agency needs to get the thread from the pool and give it
something to do. This often takes the form of task queue
(producer/consumer
pattern). The pool threads wait on the queue (a semaphore, for example,
or
an event that gets signaled when tasks are added to the queue), tasks get
added to the queue from outside. The pool threads may also able to die on
command, for which they would need to wait not only on the queue but
also on
a separate termination event. Or you could purge the queue of other tasks
and then add some lethal tasks to the queue that makes a thread suicide
when
it gets it. One poison pill for every thread in the pool <g>.
Quote
2 of 10 25/10/2021 17:14
Thread Pool - delphi http://www.delphigroups.info/2/0e/407164.html
Re:Thread Pool
Hi Peter;
Delphi Developer Where I can find a implementation for that (code source) ?
Regards;
"Peter Below (TeamB)" <[email protected]> a crit dans le
message
de news:[email protected]...
Quote
3 of 10 25/10/2021 17:14
Thread Pool - delphi http://www.delphigroups.info/2/0e/407164.html
Re:Thread Pool
news:[email protected]...
Quote
> Hi;
> I use a thread pool (with max of thread) and I would like to know :
> 1- how to recycle in Delphi 7 a thread once that it finished its activity.
Quote
4 of 10 25/10/2021 17:14
Thread Pool - delphi http://www.delphigroups.info/2/0e/407164.html
> Wait
Quote
> Notify
5 of 10 25/10/2021 17:14
Thread Pool - delphi http://www.delphigroups.info/2/0e/407164.html
private
FworkQueue:TpcQueue;
FonDone:TNotifyEvent;
someLocalObject:TlocalObject;
protected
procedure execute; override;
procedure freeLocals; virtual;
public
constructor create(workQueue:TpcQueue;onDone:TNotifyEvent);
end;
TworkThreadClass=class of TworkThread;
..
constructor TworkThread.create
(workQueue:TpcQueue;onDone:TNotifyEvent);
begin
inherited create(true);
FworkQueue:=workQueue;
FonDone:=onDone;
priority:=tpLower;
someLocalObject:=TlocalObject.create(nil);
freeOnTerminate:=true;
resume;
end;
procedure TworkThread.execute;
var inWork:TcustomWorkObject;
begin
try
repeat
FworkQueue.pop(@inWork,INFINITE); //blocks here
if (inWork=nil) then exit;
try
try
inWork.FerrorMess:='';
inWork.doWork;
except
on e:exception do inWork.FerrorMess:=e.message;
end;
finally
onDone(inWork);
end;
until false;
finally
freeLocals;
onDone(nil);
end;
end;
procedure TworkThread.freeLocals;
begin
someLocalObject.free;
end;
----------------------------------------------------------
The originator of the work sets the 'onWorkCompleted' property whe
ncreating the object. The work thread calls 'onDone', (a method of the
containing threadPool object), with the work object as the sender and the
onDone calls the 'onWorkCompleted' method property of the work object.
It is then up to the event handler that is assigned to this property to do
whatever is required to notify the originator that the work is done. If the
work is originated by a main-thread form, the event handler can use the
PostMessage API to return the work event to the form, (either directly or
via some hidden-window). If the work is originated by some other
secondary
thread, the handler can queue the work object back using whatever
mechansim
is used for the secondary thread to get its input, perhaps another P-C
6 of 10 25/10/2021 17:14
Thread Pool - delphi http://www.delphigroups.info/2/0e/407164.html
queue. This design allows the pool to be used by forms, threads, anything
really, allat the same time It also has the huge advantage that, since the
handler gets no notification of which TThread instance called it, the
apalling non-queued 'TThread.synchronize' cannot be used for notification
and so cannot create deadlocks
Once you have the above mechansim, you can assemble a 'thread pool',
eg
(untested)
----------------------------------------------------------
TthreadPool=class
private
FnumThreads:integer;
FrequestedNumThreads:integer;
FworkQueue:TpcQueue;
FthreadClass:TworkThreadClass;
FonShutdownComplete:TNotifyEvent;
FcountLock:TcriticalSection;
procedure OnWorkDone(work:TcustomWorkObject);
public
property numberOfWorkThreads:integer read FrequestedNumThreads
write FrequestedNumThreads;
constructor
create(initialThreads:integer;threadClass:TworkThreadClass);
destructor destroy; override;
procedure queueWork(work:TcustomWorkObject);
procedure shutDown(onShutdownComplete:TNotifyEvent);
end;
constructor
TthreadPool.create(initialThreads:integer;threadClass:TworkThreadClass);
var threadIndex:integer;
begin
FnumThreads:=initialThreads;
FrequestedNumThreads:=initialThreads;
FthreadClass:=threadClass;
FworkQueue:=TpcQueue.create;
FcountLock:=TcriticalSection.create;
for threadIndex:=0 to FnumThreads-1 do
threadClass.create(FworkQueue,OnWorkDone);
end;
destructor TthreadPool.destroy;
begin
FworkQueue.free;
FcountLock.free;
inherited destroy;
end;
procedure TthreadPool.queueWork(work:TcustomWorkObject);
begin
if (FnumThreads<FrequestedNumThreads) then
threadClass.create(FworkQueue,OnWorkDone);
if (FnumThreads>FrequestedNumThreads) then
FworkQueue.push(nil);
FworkQueue.push(work);
end;
procedure TthreadPool.OnWorkDone(sender:TObject);
var threadCountNil:boolean;
begin
if assigned (sender) then
begin
TcustomWorkObject(sender).FonDone(sender);
end
else
begin
FcountLock.acquire;
dec(FnumThreads);
7 of 10 25/10/2021 17:14
Thread Pool - delphi http://www.delphigroups.info/2/0e/407164.html
threadCountNil:=(FnumThreads=0);
FcountLock.release;
if assigned (FonShutdownComplete) and threadCountNil then
FonShutdownComplete(self);
end;
end;
end;
procedure TthreadPool.shutDown(onShutdownComplete:TNotifyEvent);
var purgedWork:TcustomWorkObject;
threadIndex:integer;
begin
FonShutdownComplete:=onShutdownComplete;
while (FworkQueue.pop(@purgedWork,0)) do purgedWork.free;
for threadIndex:=0 to FnumThreads-1 do FworkQueue.push(nil);
end;
----------------------------------------------------------
Using the thread pool is fairly easy. Descend from TcustomWorkObject
and
override the 'doWork' to do whatever it is needs doing. If each thread
needs some local object/s to do it's work, (eg. a socket component),
descend
from TworkThread as well and pass the new class to TthreadPool.create.
When work is required, create a TworkObject, set it up, queue it off, eg:
const
WM_SHUTDOWN=WM_APP+1;
..
TmyWork=class(TcustomWorkObject);
private
strDividend:string;
strDivisor:string;
strQuotient:string;
dividend:extended;
divisor:extended;
quotient:extended;
protected
procedure doWork; override;
end;
TmainForm=class(TForm)
private
<Delphi blurb>
protected
procedure WMAPP(ver message:Tmessage); message WM_APP;
procedure WMSHUTDOWN(ver message:Tmessage);
message WM_SHUTDOWN;
end;
..
procedure TmyWork.doWork;
begin
dividend:=strToFloat(strDividend);
divisor:=strToFloat(strDivisor);
quotient:=dividend/divisor;
strQuotient:=floatToStr(quotient);
end;
procedure TmainForm.formCreate(sender:TObject);
begin
myThreadPool:=TThreadPool.create(10,TworkThread);
end;
..
procedure TmainForm.Button1Click(sender:TObject);
var thisWork:TmyWork;
begin
thisWork:TmyWork.create(postResult);
thisWork.strDividend:=TEdit1.text;
thisWork.strDivisor:=TEdit2.text;
8 of 10 25/10/2021 17:14
Thread Pool - delphi http://www.delphigroups.info/2/0e/407164.html
myThreadPool.queueWork(thisWork);
end;
procedure TmainForm.postResult(sender:TObject);
begin
postMessage(self.handle,WM_APP,0,integer(sender));
end;
procedure TmainForm.WMAPP(var message:TMessage);
var returnedWork:TmyWork;
begin
returnedWork:=TmyWork(message.lParam);
try
if (returnedWork.errorMess='') then
Memo1.lines.add(returnedWork.strDividend+' divided by '+
returnedWork.strDivisor+' is: '+thisWork.strQuotient)
else
Memo1.lines.add('An error occurred: '+returnedWork.errorMess;
finally
returnedWork.free;
end;
end;
procedure TmainForm.postShutdown(sender:TObject);
begin
postMessage(self.handle,WM_SHUTDOWN,0,integer(sender));
end;
procedure TmainForm.WMSHUTDOWN(var message:TMessage);
begin
onCloseQuery:=nil;
close;
end;
TmainForm.OnCloseQuery(Sender: TObject; var CanClose: Boolean);
begin
CanClose:=false;
myThreadPool.shutdown(postShutdown);
end;
Note that code for a managed shutdown of the thread pool has been
added.
Peter Below is abolutely correct in saying that shutdown of complex,
multithreaded ...
read more »
Re:Thread Pool
> Controlled shutdown of such a setup can be tricky, Martin James will
likely
> hold forth on that topic in his reply to this question (which is sure to
> come, he triggers on the word "thread" <g>).
Re:Thread Pool
9 of 10 25/10/2021 17:14
Thread Pool - delphi http://www.delphigroups.info/2/0e/407164.html
Other Threads
10 of 10 25/10/2021 17:14