0% found this document useful (0 votes)
165 views

Using The Thread Pool in Delphi 7

The document discusses thread pools in Delphi and how to recycle threads. It provides an example of how a thread's execute method can loop to wait for work and then perform tasks. It also describes using Windows synchronization objects like events and semaphores to implement wait/notify functionality.
Copyright
© © All Rights Reserved
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
165 views

Using The Thread Pool in Delphi 7

The document discusses thread pools in Delphi and how to recycle threads. It provides an example of how a thread's execute method can loop to wait for work and then perform tasks. It also describes using Windows synchronization objects like events and semaphores to implement wait/notify functionality.
Copyright
© © All Rights Reserved
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 10

Thread Pool - delphi http://www.delphigroups.info/2/0e/407164.

html

Board index » delphi » Thread Pool

dBoussebh Sun, 24 Aug 2008 00:22:35 GMT

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;

dBoussebh Sun, 24 Aug 2008 01:25:54 GMT

Re:Thread Pool

I have try this code :


Delphi Developer // First call : mythread1:=mythread.create(false) (ou ...create(true) ;
mythread.resume; )
// another calls : mythread1.resume ;
// free : mythread1.terminate ; mythread1.resume ;
procedure Tmythread.execute ;
begin
repeat RealExecute ; suspend ; until terminated ;
end ;
procedure TmyThread.RealExecute ;
begin
// Mycode
end ;
But the treatment to second execute is never realized ?
"dBoussebha" <[email protected]> a crit dans le message
de
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.
> 2- how realize the wait/notify functions in Delphi 7
> Regards;

Team Sun, 24 Aug 2008 03:08:16 GMT

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

In article <[email protected]>, DBoussebha wrote:


> 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.

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- how realize the wait/notify functions in Delphi 7

You do this using Windows synchronization objects (events, semaphores)


and
API functions like WaitForSingleObject, WaitForMultipleObjects, and the
MsgWait cousins (important if your threads use COM stuff). There are
some
wrapper classes for these synchronzation objects in the SyncObjs unit.
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>).
--
Peter Below (TeamB)

2 of 10 25/10/2021 17:14
Thread Pool - delphi http://www.delphigroups.info/2/0e/407164.html

Use the newsgroup archives :


http://www.mers.com/searchsite.html
http://www.tamaracka.com/search.htm
http://groups.google.com
http://www.prolix.be

dBoussebh Sun, 24 Aug 2008 16:45:55 GMT

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

> In article <[email protected]>, DBoussebha


wrote:
> > 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.
> 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>.
> > 2- how realize the wait/notify functions in Delphi 7

3 of 10 25/10/2021 17:14
Thread Pool - delphi http://www.delphigroups.info/2/0e/407164.html

> You do this using Windows synchronization objects (events,


semaphores) and
> API functions like WaitForSingleObject, WaitForMultipleObjects, and
the
> MsgWait cousins (important if your threads use COM stuff). There
are some
> wrapper classes for these synchronzation objects in the SyncObjs
unit.
> 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>).
> --
> Peter Below (TeamB)
> Use the newsgroup archives :
> http://www.mers.com/searchsite.html
> http://www.tamaracka.com/search.htm
> http://groups.google.com
> http://www.prolix.be

Martin Jame Sun, 24 Aug 2008 18:14:32 GMT

Re:Thread Pool

Delphi Developer Quote

"dBoussebha" <[email protected]> wrote in message

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.

Do not let it terminate - loop it, preferably around a Producer-Consumer


queue wait. Other threads that want the pool to do some work then push
work
objects onto the queue. Try very hard to avoid any scheme where threads
are
expicitly pooled an a thread-safe list/array, are explicitly allocated to a
work item, have their internal fields/properties directly set with
parameters, are resumed and then have their results explicitly waited for.
All these designs are.. well.. terminally hopeless.
2- how realize the wait/notify functions in Delphi 7
Not sure exactly what you want here. The work threads can wait on a P-C
queue object which uses either a semaphore, (simple), or per-thread event,
(highest performance). In case you haven't seen it before, I added a very
simple 'minimal' P-C queue class at the end of this post.

Quote

4 of 10 25/10/2021 17:14
Thread Pool - delphi http://www.delphigroups.info/2/0e/407164.html

> Wait

Use a semaphore or TEvent per waiting thread to implement the P-C


queue.
IMHO, you should forget about those schemes that use suspend/resume
control
of threads and try to micromanage the threads from the main thread, keep
an
array of the threads, write directly from one thread into fields of another
thread
- they are an over-complex, deadlock-generating, dangerous mess. Use a
queue, hang the work threads off the queue, create/initialize work objects,
push the work objects onto the queue, process them in the threads, queue
the
work objects back to the work originator, handle them there and free them
when done.
Queued inter-thread comms are highly deadlock-resistant. You have to try
very, vary hard to lock up such systems. They also very tolerant of
temporary overloads - extra work just queues up until a thread is available
to perform it.
Beware the phrases of death:
'TThread.synchronize'
'TThread.waitFor'
'Application.processMessages'
'TThread.suspend'
'TThread.resume' (except in constructor)
'workThreads:TThreadList'

Quote

> Notify

One, very general, way is for the work object to have an


'onWorkCompleted'
TNotifyEvent that is fired, by whatever thread has finished the work, with
the work object as the sender, eg, (untested)
----------------------------------------------------------
TcustomWorkObject=class
private
FonWorkCompleted:TNotifyEvent;
FerrorMess:string;
protected
procedure doWork; abstract;
public
property errorMess:string read FerrorMess;
property onWorkCompleted:TNotifyEvent read FonWorkCompleted;
constructor create(onWorkCompleted:TNotifyEvent);
end;
TworkThread=class(TThread)

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 »

Martin Jame Sun, 24 Aug 2008 18:37:41 GMT

Re:Thread Pool

Delphi Developer Quote

> 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>).

Funny you should say that <g>


Rgds,
Martin

Martin Jame Sun, 24 Aug 2008 20:12:32 GMT

Re:Thread Pool

9 of 10 25/10/2021 17:14
Thread Pool - delphi http://www.delphigroups.info/2/0e/407164.html

Simple demo in 'attachments', essentially the same code as the 'untested'


example code I posted earlier but with the typos, missing constructors, type
Delphi Developer mismatches etc etc. fixed <g>
Rgds,
Martin

Other Threads

1. Thread Pooling with Indy


2. UDPServer Thread Pools and Indy
3. COM events and Thread pooling / completion ports
4. Thread Pool
5. thread pool and to supervise a directory
6. Increasing the size of a thread pool dinamically
7. Thread pooling and Indy
8. Thread-pools for remote data modules
9. Win2k Thread Pooling
10. Transfer files with a thread pool possible in Indy ?

10 of 10 25/10/2021 17:14

You might also like