Click here to Skip to main content
Click here to Skip to main content

A thread pool implementation

By , 25 Apr 2003
 

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

  1. Insert ThreadPool.cpp & ThreadPool.h in project.
  2. 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.
  3. 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.
  4. Whenever a job is to be assigned, call AssignTask() with a _cdecl function pointer of the client.
  5. Function will be executed from any of the free thread in thread pool.
  6. If DATA members need to be sync, in the submitted function in client, that has to be done by the client.
  7. 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.
  8. 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.
  9. 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.

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here

About the Author

Rajeev Sadasivan
Japan Japan
Member
No Biography provided

Sign Up to vote   Poor Excellent
Add a reason or comment to your vote: x
Votes of 3 or less require a comment

Comments and Discussions

 
You must Sign In to use this message board.
Search this forum  
    Spacing  Noise  Layout  Per page   
Generaltwo errors after I read itmemberhephaestus_lee22 May '07 - 23:16 
Thank the author provide the source.
I found two errors.
 
1.need set a event after setup message queue.
PostThreadMsg must be called after waitfor this event
 
2.TerminalThread is a critical error,never use it.
the thread should end by itself.
 
this line is error:
while (WM_QUIT != GetMessage(&msg, NULL, 0, 0) )
so it never ends.
After fix it,you can waitforxxx (hThread,INFINITE) to wait all threads end normally.
 
It runs perfectly after fixed them.
 


QuestionRe: two errors after I read itmembercharian092028 Jun '07 - 22:06 
hi ...
 
could you please post the correct codes?
 
i'm trying to use this class and i'm not able to run another job. Frown | :(
 
thanks.
GeneralRe: two errors after I read itmemberMrZhangjianliang20 Jul '07 - 0:52 
wow, yes,
I also found the 1th error, but didn't find the 2th error,
hephaestus_lee, you are so good.
Killdog1982@gmail.com
 

 
Jianliang
GeneralQuestionsmemberName_fa16 Feb '07 - 14:00 
1. Why do you use mutexes instead of critical sections?
2. Why do you use UI message queue to pass your messages instead of creating your own?
GeneralRe: QuestionsmemberRajgkk20 Feb '07 - 0:16 
I think Mutex is used to make IPC compatible if required later.
GeneralRe: QuestionsmemberName_fa20 Feb '07 - 7:16 
If you would want to make this IPC-compatible, you would have to rewrite it significantly anyway.
GeneralSo make a static entry in your classmemberFrank Driesens1 May '03 - 0:43 
Why don't you define a static bool DoSecondJob(UINT taskId, LPVOID pData) in your header ?
Works perfectly Smile | :) At least this is how I did it in my workpool (pre ATL7.. Smile | :)
But I did split the workpool and worker threads from the tasklist and subsequent tasks.. Smile | :)
GeneralCThreadPool - ATL7memberTW27 Apr '03 - 1:45 
Use it!
GeneralRe: CThreadPool - ATL7sussAnonymous30 Apr '03 - 7:07 
It works only on NT 4.0 Poke tongue | ;-P
GeneralRe: CThreadPool - ATL7memberRajgkk20 Feb '07 - 0:14 
No it works for all

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Permalink | Advertise | Privacy | Mobile
Web04 | 2.6.130516.1 | Last Updated 26 Apr 2003
Article Copyright 2003 by Rajeev Sadasivan
Everything else Copyright © CodeProject, 1999-2013
Terms of Use
Layout: fixed | fluid