License : Creative Commons Attribution 4.0 International (CC BY-NC-SA 4.0)
Copyright : Frédéric Pennerath, CentraleSupelec
Last modified : April 19, 2024 10:22
Link to the source : threads.md

Implementation of a thread pool

Installation

Download thread_pool.zip and generate a suitable build environment using CMake:

unzip thread_pool.zip ; cd thread_pool
mkdir build ; cd build
cmake ../src
make
./pool_test 1  # Nothing happens as nothing has been implemented yet

The code to edit is in subdirectory src. The subdirectory solution contains a possible solution.

Introduction

This labwork is about the synchronization of C++11 threads based on mutexes and condition variables to handle problems of race condition, deadlock, etc. The goal of the labwork is to design a thread pool.

When a parallelizable computation is short, the fixed time (overhead) required to create and destroy threads and their resources (stack, thread-local variables, etc) is no longer negligible. One solution is to use a thread pool. When it is created, the pool is “filled” with a certain number of threads which are kept on standby. Every time a computational task is posted to the pool, an available thread is woken up and processes the task before going back to sleep once the computation is complete. To handle the case where all the threads are busy, the pool also has a queue storing the computations waiting to be run. The threads in the pool are kept alive as long as the pool itself is not destroyed. A pool is destroyed only once all the ongoing tasks are complete.

Designing the pool

We would like to be able to program the thread pool as follows:

void A() { // Run a computation for 100ms ... }
void B() { // Run a computation for 200ms ... }
void C() { // Run a computation for 100ms ... }

{
  ThreadPool pool{2}; // Create a thread pool of two threads
  pool(A); // Run function A in thread 1 of the pool
  pool(B); // Run function B in thread 2 of the pool
  pool(C); // Run function C in thread 1 of the pool after 100ms (once thread 1 has finished to run A)
}
// The pool is destroyed here, uniquely when all tasks have been completed, that is after 200ms

Complete class ThreadPool in the ThreadPool.hpp and ThreadPool.cpp files to conform to the previous example. Test using function test1.

Recommendations:

Performance test

We want to compare the performance of a thread pool against the “classic” instantiation of threads. To do this we need a join method that puts the calling thread on hold as long as the pool has a task to process or is being processed, as in the following example.

ThreadPool pool{2}; // Create a thread pool of two threads
pool(A)(B)(C); // Run functions A, B and C
pool.join(); // Wait until A, B and C are completed

Complete method join of class ThreadPool and evaluate the performance of the thread pool using function test2.

Frédéric Pennerath,