What is a thread pool? Exactly that, a pool of threads. You may have heard of terms like object pooling, thread pooling, car pooling (oops), anyway, the idea behind pooling is that you can re-use the objects which may be threads or database connections or instances of some class. Now why would we ever want to re-use such things? The most important reason would be that creating such an object might take up a lot of resources or time, so, we do the next best thing. We create a bunch of them initially and call the bunch a pool. Whenever someone (some code) wants to use such an object, instead of creating a new one, it gets it from the already existing bunch (pool).
The code for this was written quite a while back - Sept 2005! After seeing recent action on this article, I decided to update it. Thanks to everyone who has appreciated, asked questions and most of all pointed out defects in the code.
The latest updates are listed below:
shutdown event is not a named event to allow creation of multiple thread pools across processes.
- Individual thread wait handles are named as
Create() method to check
INVALID_HANDLE_VALUE when using
Create() method to return
false if we fail to create any of the threads.
- Added virtual destructor to
Destroy() to delete
IRunObject object is
- The sample application has been revamped. I must admit it wasn't very understandable. It's been made lot more simpler and demonstrates the use of the thread-pool better.
Usually when I develop something, there is a fairly good reason behind it like my project demanded it or I did it for someone who asked me about it, but this thread pool was a result of neither of these. Actually, around a year back, I had attended an interview with a company where I was asked this question. Well at that time, I didn't really think of it seriously, but a few days back the question came back to me, and this time I decided it was time to get my answer working.
Using the Code
- threadpool.h & threadpool.cpp - defines the
CThreadPool class. This class is the main class which provides the thread pooling facilities.
- ThreadPoolAppDlg.cpp - see the
OnOK() method. This method makes use of the thread pool.
This example was written using VC++ 6.0. The source has now been updated for Visual Studio 2008. The thread pool class itself does not make use of any MFC classes so you should be able to use the thread pool for pure Win32 applications also.
A Little Explanation of How the Pool Works
First, you need to create an instance of the thread pool. While creating it, you have the option of specifying the number of threads which will live in the pool. The default is
10. You can also specify whether to create the pool right now or later. If you choose to create it later, you will need to call the
Create() method to actually create the threads in the pool.
Once the pool is created, the following things have already happened:
- The specified number of threads have been created.
- These threads have been added to an internal list of threads.
- A list has been created which will store the user requests for execution.
- All the threads are waiting eagerly for someone to add a work item for execution.
Once a new item has been given to the thread pool, the pool looks for the first available (free) thread. It sets off an event which makes the thread go and pop off the work item from the list of requests and immediately go about executing the request. Once execution finishes, the thread marks itself as 'free' and checks the request-list once for any pending requests. This keeps happening for the entire lifetime of the pool.
Using the Pool
Once the pool is created, you can give it work to do using two different ways.
Write a function of the following form:
DWORD WINAPI MyThreadFunc1(LPVOID param)
UserPoolData* poolData = (UserPoolData*)param;
LPVOID myData = poolData->pData;
while(poolData->pThreadPool->CheckThreadStop() == false)
Once this is done, suppose the instance of the thread pool is
gThreadPool, then call the
Run() method of the pool. E.g.:
CMyOwnData* PointerToMyOwnData = new CMyOwnData();
So, in this case what we specify are:
- the function to execute
- the data which will be passed into the function while it executes in a '
LPVOID param in the
Write your own class but make sure it derives from
IRunObject (declared in RunObject.h). E.g.:
class CRunner : public IRunObject
for(int nIndex=0; nIndex < 10000; nIndex++)
Once you derive a class from
IRunObject you need to write code for the
void Run() method and also for the
bool AutoDelete() method. Write your business logic in the
Run method. When the pool picks up your object, it will call its
Run() method. After the
Run() method finishes, the pool will call the
AutoDelete() method. If this method returns
true, the pool will use C++
delete to free the object. If the method returns
false, the pool will not do anything to the object. Finally, you need to add the object to the queue. E.g.:
CRunner* runner = new CRunner();
runner->m_hWnd = hListBox3;
To run the demo application, go to the bin folder. Rename the file ThreadPoolApp.exe.txt to ThreadPoolApp.exe and double-click it.
If for some reason the executable is not present, please build the source code. The source has been compiled using Visual Studio 2008 SP1.
Explanation of the Demo
The demo application is a simple dialog based MFC application. It's got three buttons and three list boxes:
- The dialog has a thread-pool of size 2 threads created. The work that is submitted is filling the list boxes with numbers from 0 to 9. To add work to fill the first list-box, click the 'Add Work' button under it. Similarly, it is possible to add work for the other list-boxes. Since the initial threadpool contains only 2 threads, if you queue up work items for all the 3 list-boxes, you see the last list-box getting filled only after the first two list-boxes have been filled.
- 'Create Pool' button: Clicking this button just destroys the existing pool and creates a new one. Enter the number of thread you want for the new pool and click this button. This will cause the existing threadpool to go into a 'destroying' state. The user-code which fills the listboxes check for the state by calling
CheckThreadStop() on the threadpool object. If
true, the user-code halts its execution.
- 'Destroy Pool' button: Clicking this button destroys the existing pool. All work items stop their execution. All threads are destroyed. Adding new work items have no effect since the pool itself is destroyed and work-items are no longer queued.
- 'Quit' button: Closes the application.
- 17th October, 2005: Initial post
- 31st March, 2010: Article updated
- 8th January, 2011: Article updated
- Fixed some memory related bugs
- Added ability for user-code to react to pool-destruction event
- Added new functions
int GetWorkingThreadCount() and
bool CheckThreadStop() to
- Updated code to run on Visual Studio 2008
- 4th February, 2011: Updated source code
C++ is great fun. Hope you all enjoyed this article as much as I had fun writing it.
Please mail your comments to firstname.lastname@example.org. I'd love to hear from you.