Introduction
In many of our applications we use multi threading for better performance.
Most of us already knows and using multi threading applications. And we know
that thread creation is a resource intensive job. It is always better to reuse
the threads, and most of us do this also. This article talks about such a thread
pooling (worker thread) architecture. It will be great if you can provide let me
know about any bugs that are found and any improvements that can be done to
this.
Architecture in brief
The main architecture of the thread pool is given below in the class
diagram.
As you can see in the above class diagram the application has mainly three
classes, they are
-
CThPool
class : This is the main thread pool class. To
use thread pool you have to only create an instance of this class.
-
CThread
class : This class encapsulates one thread. You
don't have to create any instance of this class. The CThPool
class
will return a CThread
class, which you should use. You should not
also destroy the CThread
object that you received from the
CThPool
class instance.
-
CThTarget
: This is the base class of all the target
objects which can be passed to the CThread
class as target. This is
an abstract class with run()
as the pure virtual method. You should
implement the run()
in your inherited class. As shown in the class
diagram for the class CYourClass
.
Using the code
To use the thread pool code we have to do the following
- Create an instance of the pool. Refer the sample code below.
CThpool *m_pThPool;
m_pThPool = new CThPool();
- Change the maximum pool size if you want to change the maximum number of
threads that can be created for use at any instance of time for your system. By
default this is 5. You can also change the pool size at run time for better
performance as you require.
m_pThPool->setMaxPoolSize(10);
- Create a class as shown in the class diagram, which will inherit the
CThTarget
class and implement the pure virtual function
void
run
(). Refer the sample code below class CThTargetSuper : public CThTarget
{
public:
CThTargetSuper();
virtual ~CThTargetSuper();
void run();
}
void CThTargetSuper::run()
{
}
- Create an instance of your class, which you have defined as above
CThTargetSuper* pTarget = new CThTargetSuper();
- When you need to use a thread to do some process which is defined on the
run()
function of your class. Then get an instance of
CThread
class from the pool by calling the function
getThread()
. Then call the setTarget()
function of the
CThread
instance you received and pass the reference of your
inherited CThTarget
class. CThread * pTh=NULL;
pTh = m_pThPool->getThread();
if( pTh!=NULL)
pTh->setTarget(pTarget);
Note : As you see in the above code the CThread
instance
received from the CThPool::getThread()
function may return
NULL
. This is in case of all the threads are busy and no free
thread is available and the pool's current size is already achieved the Max pool
size. If you want the job should be processed then you can try again after some
time. The code can be modified as follows. But if this code is executed by the
main GUI thread then the while loop may cause problem if the threads are busy
for long time. In such cases this code should be executed in a different thread.
CThread * pTh=NULL;
pTh = m_pThPool->getThread();
while(pTh ==NULL)
{
Sleep(100);
pTh = m_pThPool->getThread();
}
if( pTh!=NULL)
pTh->setTarget(pTarget);
- After
CThread::setTarget()
function call the run()
function of the CThTarget
type object will run in a separate
thread. The thread will automatically return to the pool when the
run()
function finishes.
Note : Once the thread will be completed the CThTarget
object cannot be used again. This is because once the run()
function finishes the thread deletes the instance of the CThTarget
.
If you want to use the CThTarget
object later then you have to tell
the thread explicitly by setting the auto delete flag of CThTarget
to FALSE
as given in the code below.
CThTargetSuper* pTarget = new CThTargetSuper();
pTarget->setAutoDelete(FALSE);
The default value is TURE
. You can check this at any time by
calling AutoDelete()
. It returns TRUE
if the flag is
set as TRUE
.
Never call setTarget()
again on the same CThread
instance that you received from the last getThread()
object. Always
use getThread()
on CThPool
instance to get a
CThread
object and then use setTarget()
on that object
only once.
Additional Features
How To Set Max Free Pool Size
Some times our application may need let 100 threads but most of the time the
required thread is less than 20. In that case it is no need to create and keep
100 threads always in the pool. In such cases we can define the maximum free
pool size to 20.This will make sure that not more than 20 idle threads will be
available in the free pool. If required more number of threads will be created
as on demand. Sample code is given below.
m_pThPool = new CThPool();
m_pThPool->setMaxPoolSize(100);
m_pThPool->setMaxFreePoolSize(20);
Getting Statistics
Some times we may require statistics to know at any instance of time how many
threads are busy and how many threads are free, what is the pool size now, etc.
etc. We can get such information by calling some methods of pool. In the sample
application the statistics are also used. The functions that are available to
get the statistical information are
int CThPool::getCurPoolSize()
int CThPool::getMaxPoolSize()
int CThPool::getMaxFreePoolSize()
int CThPool::getFreePoolSize()
About The Demo Program
The demo program uses one thread pool. Initially the pool has started with
one thread. And the Max pool size is 5. And the maximum free thread size is 2.
In the demo program CThTargetSuper
is the class, which extends
CThTarget
and implement the run()
method. The run
method simply increments the progress bar after sleeping 100 ms till it reaches
the end. If more number of threads will be assigned then the progress bar will
move fast. The run()
methods ends when the progress bar reaches the
end of the bar. And the after that the instance of the
CThTargetSuper
gets deleted by the thread (As the Auto Delete flag
is TRUE
by default) and the threads returns to the free pool and
remains in suspended mode till another work get assigned. The thread may also
exit if the number of threads that are present in the free pool is more than the
maximum limit.
Executing the Demo Program
After you start the program first create the pool by clicking the
CreatePool button. Then you can click on the
AssignThread button to assign a new thread. You can not assign
more threads than the Max Pool size. The current number of threads busy in the
pool is displayed in the statistics along with the idle threads and max pool
size. You can change the the maximum pool size by clicking the button "+" or "-"
. (Upper limit is defined as 20). You can also change the maximum free pool size
by clicking the corresponding "+" / "-" button. You can also press the
Refresh button to refresh the statistics. The statistics also
gets automatically updated around every 2 seconds.
To repeat the process click on the Reset button so that the
progress bar will be reset to initial position. And you can assign the threads
again to increase the progress bar's progress.