Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / programming / threads

Thread Pool

4.92/5 (27 votes)
19 Aug 2013CPOL5 min read 66.8K   3.1K  
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)