Click here to Skip to main content
15,868,016 members
Articles / General Programming / Threads

Thread Pool

Rate me:
Please Sign up or sign in to vote.
4.92/5 (27 votes)
19 Aug 2013CPOL5 min read 63.6K   3.1K   73   16
Thread pool class

Introduction

One of my applications will take periodic data backup to multiple destinations (CD, USB, NETWORK SHARE). When I wrote a single threaded application for this, it took more than two hours to complete a single backup. It was a challenge for me and the research to improve the performance ended up in multi-threading. The result was amazing, the backup completed within 30 minutes.

The beginning of the development, the backup period was one day, later I forced to increase the frequency to one hour. Now I noticed that the application has an overhead of thread creation and destruction due to small frequency. How to solve it? Why threads can't be re-used? These thoughts ended up in a thread pool.

Whether Thread Pool is Needed for You?

  • Do you have multiple requests to be processed repeatedly and parallel?
    • Each request can be processed independently?
  • Do you have waiting IO/File operations?

If you have answered yes, then you can go for thread pool. The application you are going to design should have low coupling for easy implementation of thread pool.

Background

In the career of a programmer, he will be forced to create at least a single thread pool. In my case also, the same happened. I forced to create a thread pool in C++. As usual , I logged into Google for downloading the same, but this time the result was negative. Even though there are lots of thread pool applications, I could not find a suitable one for me. It brings me here to write this tip.

Why We Need a Thread Pool?

Normally, most of the IO operations (file, disk, etc.) will take a long time to complete; so in a single threaded application, system resources are in waiting state during IO operations. This waiting time of system resources(memory, processor, etc.) can be effectively utilized by implementing multiple threads. So while IO thread is performing IO operation, non thread can utilize memory and processor effectively.

A thread pool will be effective in the following situations:

  • An application needs to avoid thread creation and destruction time in its scenarios.
  • An application that is parallel and can dispatch a large number of small work items asynchronously.
  • An application that creates and destroys a large number of threads that each run for a short time. Using the thread pool can reduce the complexity of thread management and the overhead involved in thread creation and destruction.
  • An application that processes independent work items in the background and in parallel.
Thread Pool Design

The thread pool will create specified number of threads and wait for request. Once a request is posted to thread pool, one thread will be active and start execution. After the completion of the request processing, the thread will go back to waiting state and wait for next request to be queued. When user requested destroy, all threads will be exited from the thread pool.

The class diagram is shown below:

Image 1

ThreadPool

This class will create, manage and destruct thread pool. The user (your application) of the ThreadPool should create an object of ThreadPool class in their application. While creating thread pool, user can specify the thread count. This thread pool will support maximum of 64 threads even though it can reduce the count to minimum for avoiding the overhead of thread switching and system resources usage.

AbstractRequest

It stands for a request in thread pool. The client application should derive this class and write the code to be executed inside the thread in Execute() function. The user should ensure thread safety of this function by using critical section object provided. Also cancel handling can be done by using IsAborted() function meaning fully. Then post the derived abstract request instance to thread pool for processing.

Canceling can be done by calling Abort() function.

Logger

The thread pool has a facility for error and information logging. The default log location is debugger window. This can be overridden by creating a user-defined class, which is derived from Logger class. Then override LogError() and LogInfo() with needed logging mechanism.

Give the instance of logger class to thread pool during the creation.

How can thread pool be in your application?

Image 2

The user application will hold an instance of ThreadPool class. While receiving create request, it will create specified number of threads and each thread will wait for request queuing. Once a request received, then one thread will come out the wait state and start processing the request. After completion of the request processing, the thread will go back to the waiting state.

The user can abort processing of request by calling Abort() method of AbstractRequest class at any time. The thread pool will not delete request after completion of processing.

Using the Code

  • Create()
    C++
    // Create thread pool with specified number of threads.
    bool Create( const unsigned short usThreadCount_i, Logger* pLogger_io = NULL ); 

    This function will create a specified number of threads in waiting state. If the Logger is specified, then it will be used for error logging. This function will return false in case of failure. The maximum number of threads is limited to 64.

  • Destroy()
    C++
    // Destroy the existing thread pool.
    bool Destroy(); 

    This function will abort all requests and destroy the thread pool. It will return false in case of failure.

  • PostRequest()
    C++
    // Post request to thread pool for processing.
    bool PostRequest( AbstractRequest* pRequest_io ); 

    This function will post specified request to thread pool for processing and it will return false in case of failure.

Dependency

ThreadPool.h includes windows.h, list.h (STL) and string.h (STL)

How the thread pool can be used in your application?

  • Include ThreadPool.h and ThreadPool.cpp in your application.
  • The thread pool will log error and information in debugger window and this behavior can be overridden if needed. This default behavior can change by deriving a class from Logger, and derive LogError() and LogInfo() functions.
  • Create thread pool by calling Create() function. Provide the Logger instance if needed.
  • Create a class derived from AbtractRequest and derive Execute() function which acts as thread procedure.
  • Post the instance of the class which derived from AbtractRequest to thread pool for processing using PostRequest() function. User can post requests without any limits, but active request count will be same as thread count.
  • Once processing is completed, you can destroy the thread pool using Destroy() function.

ThreadPoolDemo test application is available for familiarizing the usage of ThreadPool class.

Points of Interest

This thread pool is implemented for Windows operating system and it can be ported to Linux or Apple platform.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Software Developer
India India
C++ developer

Comments and Discussions

 
QuestionError in ThreadPool::ThreadProc Pin
PJ Arends25-Feb-24 15:23
professionalPJ Arends25-Feb-24 15:23 
QuestionTo run the demo properly, you need to add this line #include <string> in ThreadPool.h, Request.cpp for VS2017 Pin
kuelite28-Jan-22 3:34
kuelite28-Jan-22 3:34 
SuggestionDestroy request object(s) Pin
simonchen.net1-Nov-17 22:25
simonchen.net1-Nov-17 22:25 
QuestionThread pool Abort() Pin
vigers16-Dec-15 23:11
vigers16-Dec-15 23:11 
AnswerRe: Thread pool Abort() Pin
Suresh P V18-Jan-16 22:34
Suresh P V18-Jan-16 22:34 
QuestionHow can I tell if all the work is completed Pin
mkdym24-Nov-14 21:51
mkdym24-Nov-14 21:51 
AnswerRe: How can I tell if all the work is completed Pin
Suresh P V24-Nov-14 22:06
Suresh P V24-Nov-14 22:06 
GeneralRe: How can I tell if all the work is completed Pin
mkdym24-Nov-14 22:19
mkdym24-Nov-14 22:19 
Questiontranslation Pin
candy ang4-Jul-14 5:20
candy ang4-Jul-14 5:20 
AnswerRe: translation Pin
Suresh P V4-Jul-14 16:05
Suresh P V4-Jul-14 16:05 
SuggestionPassing data to each task executed in parallel in separate thread Pin
RBPRO17-Nov-13 14:06
RBPRO17-Nov-13 14:06 
I implemented a pool of threads for the first time. After trying many thread pools available from the web, only your project was fully operational and easy to implement in VS2008 Big Grin | :-D . However, I searched the way to pass and retrieve data from the Execute task, then I developer the following variant from your code:
In the main class of the application, I declared a THREADSTR structure as follow:
class CThreadPoolTestApp : public CWinApp
{
typedef struct THREADSTR
{ bool IsTaskEnded; // any other data required for the task
}
THREADSTR MyThreadData[20];

.... etc...
The structure MyThreadData is a vector of 20 elements comprising all required data and parameters for each task. Each element of the vector is allocated to a specific task up to 20 tasks in my example.

In another application (document class for example) I add the required information to run the tasks several times as below:
void CThreadPoolTestDoc::OnFileExecute()
{ LPVOID pParam;
int i;
bool isTerminated;
CThreadPoolTestApp* pApp= (CThreadPoolTestApp*) AfxGetApp();
// get the pointer of the application handling the data structure MyThreadData
if(!IsPoolThreadCreated)// Create 10 threads in the pool
IsPoolThreadCreated = m_ThreadPool.Create(10);
for (i=0;i<20;i++) // reset the flag of the end tasks
{ pApp->MyThreadData[i].IsTaskEnded=false;
}
for (i=0;i<20;i++)
{ pParam= &(i); // note the param is the task number to address the values in the data structure
if(!pReq[i] ) // generate a vector of tasks
pReq[i] = new RequestFnct(pParam); // the task number is supplied to the constructor
m_ThreadPool.PostRequest(pReq[i]);
}
isTerminated=false; // need to wait for the results from all tasks
while (!isTerminated)
{ isTerminated=true;
Sleep(10); // wait for all task being completed
for (i=0;i<20;i++)
if(!pApp->MyThreadData[i].IsTaskEnded) isTerminated=false;
}
// ... other computation requiring the results of the executed tasks...
}
The small difference with your code is the addition of one parameter passed in the constructor of the class derived from the AbstractRequest. This parameter can be the pointer of the full structure but in my case I only passed the task number associated with the structure of data. The Execute function uses this task number to exchange data with the main program. Is there other simple way to exchange data with the Execute task? D'Oh! | :doh:
Roger

GeneralRe: Passing data to each task executed in parallel in separate thread Pin
Suresh P V20-Nov-13 16:28
Suresh P V20-Nov-13 16:28 
GeneralMy vote of 5 Pin
H.Brydon5-Sep-13 20:37
professionalH.Brydon5-Sep-13 20:37 
GeneralRe: My vote of 5 Pin
Suresh P V6-Sep-13 6:56
Suresh P V6-Sep-13 6:56 
QuestionThread Pool Pin
george bolovan26-Aug-13 10:01
professionalgeorge bolovan26-Aug-13 10:01 
AnswerRe: Thread Pool Pin
Suresh P V30-Aug-13 7:24
Suresh P V30-Aug-13 7:24 

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

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.