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, after WM_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.
CMyTestView::CMyTestView()
{
m_Pool.Initialize(10,
20,
2000 );
}
void CMyTestView::OnFirstJob()
{
THREAD_POOL_TASK taskInfo = {TEST_DO_TASK1, DoFirstJob, LPVOID(this)};
m_Pool.AssignTask(&taskInfo );
}
void CMyTestView::OnSecondJob()
{
THREAD_POOL_TASK taskInfo = {TEST_DO_TASK2, DoSecondJob, LPVOID(this)};
m_Pool.AssignTask(&taskInfo );
}
bool DoFirstJob(UINT taskId, LPVOID pData)
{
CMyTestView *pView = (CMyTestView*)pData;
return pView->DoJob(taskId);
}
bool DoSecondJob(UINT taskId, LPVOID pData)
{
CMyTestView *pView = (CMyTestView*)pData;
return pView->DoJob(taskId);
}
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,
20,
2000 );
}
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.