A thread pool implementation






2.33/5 (6 votes)
Apr 26, 2003
2 min read

93362

2673
A simple thread pool
Introduction
This article is about a re-usable class for thread pooling. It controls the number of threads used to a prescribed level and schedules the requests to jobs to any of the thread in the pool.
Background
While I was writing an application which has many number of threads, I found it difficult to manage a lot of threads. I thought I should limit the number of threads to a certain level and queue the jobs to the threads. So I decided to implement this re-usable thread pooling class.
Using the code
- Insert ThreadPool.cpp & ThreadPool.h in project.
- Project Settings
- On the Project menu, Click Settings.
- In the Project Settings dialog box, click the C/C++ tab.
- Select Code Generation from the Category drop-down list
box.
- From the Use Run-Time Library drop-down box, select
MULTITHREADED.
- Click OK.
- Call
Initialize()
with required thread count; Default value is 1. Maximum number of threads and tasks can be recommended. The tasks list size will grow if required. Default values are 24 threads and 1024 task information. - Whenever a job is to be assigned, call
AssignTask()
with a_cdecl
function pointer of the client. - Function will be executed from any of the free thread in thread pool.
- If DATA members need to be sync, in the submitted function in client, that has to be done by the client.
- When thread count need to be increased/decreased, just call
Initialize
again. NO Resource/Memory LEAK. The current threads finishes execution; then it will be aborted. - To destroy the thread pool, without deleting threadpool object,
call
UnInitialize()
. This will delete all resources. If threads are still working, afterWM_QUIT
posting and a small waiting, it terminates all threads. UnInitialize()
is also called from destructor.
In the demo application, use the "work" menu to assign sample jobs to thread pool.
/* S A M P L E - C O D E*/ CMyTestView::CMyTestView() { m_Pool.Initialize(10, /* Required number of threads */ 20, /* Max threads expected; Default = 24*/ 2000 /* Max tasks expected at a time; Default=1024 */); // Now only 10 thread will be in the pool; // Second param 20 is just for internal memory allocation. } void CMyTestView::OnFirstJob() { THREAD_POOL_TASK taskInfo = {TEST_DO_TASK1, DoFirstJob, LPVOID(this)}; m_Pool.AssignTask(&taskInfo ); } // task submitter second void CMyTestView::OnSecondJob() { THREAD_POOL_TASK taskInfo = {TEST_DO_TASK2, DoSecondJob, LPVOID(this)}; m_Pool.AssignTask(&taskInfo ); } // Thread pool call back. - for first task bool DoFirstJob(UINT taskId, LPVOID pData) { CMyTestView *pView = (CMyTestView*)pData; return pView->DoJob(taskId); } // Thread pool call back. - for second task bool DoSecondJob(UINT taskId, LPVOID pData) { CMyTestView *pView = (CMyTestView*)pData; return pView->DoJob(taskId); } // Actual execution goes here. bool CMyTestView::DoJob(UINT taskId) { switch(taskId) { case TEST_DO_TASK1: { m_Message = _T("Doing FIRST job"); Invalidate(0); int nCount = 1; while (nCount--) { Sleep(1000); Beep(100, 1000); } } break; case TEST_DO_TASK2: { m_Message = _T("Doing SECOND job"); Invalidate(0); int nCount = 2; while (nCount--) { Sleep(2000); Beep(200, 2000); } } } return true; } void CMyTestView::OnResetWorkLoad() { m_Message = _T("On Reset Work Load"); Invalidate(0); m_Pool.Initialize(5, /* Required number of threads */ 20, /* Max threads expected; Default = 24*/ 2000 /* Max tasks expected at a time; Default=1024 */); // Now only 5 thread will be in the pool; // Out of previous TEN, FIVE will be removed. }
Points of Interest
I learned the usefulness of thread pooling. The annoying thing is that global functions are needed for callback functions. I managed it with the help of static functions within the classes.