3D City v-1.0
Computer Graphics Project
thread_pool Class Reference

A C++17 thread pool class. More...

#include <thread_pool.hpp>

Public Member Functions

 thread_pool (const ui32 &_thread_count=std::thread::hardware_concurrency())
 Construct a new thread pool. More...
 
 ~thread_pool ()
 Destruct the thread pool. More...
 
ui64 get_tasks_queued () const
 Get the number of tasks currently waiting in the queue to be executed by the threads. More...
 
ui32 get_tasks_running () const
 Get the number of tasks currently being executed by the threads. More...
 
ui32 get_tasks_total () const
 Get the total number of unfinished tasks - either still in the queue, or running in a thread. More...
 
ui32 get_thread_count () const
 Get the number of threads in the pool. More...
 
template<typename T1 , typename T2 , typename F >
void parallelize_loop (const T1 &first_index, const T2 &index_after_last, const F &loop, ui32 num_blocks=0)
 Parallelize a loop by splitting it into blocks, submitting each block separately to the thread pool, and waiting for all blocks to finish executing. More...
 
template<typename F >
void push_task (const F &task)
 Push a function with no arguments or return value into the task queue. More...
 
template<typename F , typename... A>
void push_task (const F &task, const A &...args)
 Push a function with arguments, but no return value, into the task queue. More...
 
void reset (const ui32 &_thread_count=std::thread::hardware_concurrency())
 Reset the number of threads in the pool. More...
 
template<typename F , typename... A, typename = std::enable_if_t<std::is_void_v<std::invoke_result_t<std::decay_t<F>, std::decay_t<A>...>>>>
std::future< bool > submit (const F &task, const A &...args)
 Submit a function with zero or more arguments and no return value into the task queue, and get an std::future<bool> that will be set to true upon completion of the task. More...
 
template<typename F , typename... A, typename R = std::invoke_result_t<std::decay_t<F>, std::decay_t<A>...>, typename = std::enable_if_t<!std::is_void_v<R>>>
std::future< R > submit (const F &task, const A &...args)
 Submit a function with zero or more arguments and a return value into the task queue, and get a future for its eventual returned value. More...
 
void wait_for_tasks ()
 Wait for tasks to be completed. More...
 

Public Attributes

std::atomic< bool > paused = false
 An atomic variable indicating to the workers to pause. More...
 
ui32 sleep_duration = 1000
 The duration, in microseconds, that the worker function should sleep for when it cannot find any tasks in the queue. More...
 

Private Types

typedef std::uint_fast32_t ui32
 
typedef std::uint_fast64_t ui64
 

Private Member Functions

void create_threads ()
 Create the threads in the pool and assign a worker to each thread. More...
 
void destroy_threads ()
 Destroy the threads in the pool by joining them. More...
 
bool pop_task (std::function< void()> &task)
 Try to pop a new task out of the queue. More...
 
void sleep_or_yield ()
 Sleep for sleep_duration microseconds. More...
 
void worker ()
 A worker function to be assigned to each thread in the pool. More...
 

Private Attributes

std::mutex queue_mutex = {}
 A mutex to synchronize access to the task queue by different threads. More...
 
std::atomic< bool > running = true
 An atomic variable indicating to the workers to keep running. More...
 
std::queue< std::function< void()> > tasks = {}
 A queue of tasks to be executed by the threads. More...
 
ui32 thread_count
 The number of threads in the pool. More...
 
std::unique_ptr< std::thread[]> threads
 A smart pointer to manage the memory allocated for the threads. More...
 
std::atomic< ui32 > tasks_total = 0
 An atomic variable to keep track of the total number of unfinished tasks - either still in the queue, or running in a thread. More...
 

Detailed Description

A C++17 thread pool class.

The user submits tasks to be executed into a queue. Whenever a thread becomes available, it pops a task from the queue and executes it. Each task is automatically assigned a future, which can be used to wait for the task to finish executing and/or obtain its eventual return value.

Definition at line 36 of file thread_pool.hpp.

Member Typedef Documentation

◆ ui32

typedef std::uint_fast32_t thread_pool::ui32
private

Definition at line 37 of file thread_pool.hpp.

◆ ui64

typedef std::uint_fast64_t thread_pool::ui64
private

Definition at line 38 of file thread_pool.hpp.

Constructor & Destructor Documentation

◆ thread_pool()

thread_pool::thread_pool ( const ui32 &  _thread_count = std::thread::hardware_concurrency())
inline

Construct a new thread pool.

Parameters
_thread_countThe number of threads to use. The default value is the total number of hardware threads available, as reported by the implementation. With a hyperthreaded CPU, this will be twice the number of CPU cores. If the argument is zero, the default value will be used instead.

Definition at line 50 of file thread_pool.hpp.

◆ ~thread_pool()

thread_pool::~thread_pool ( )
inline

Destruct the thread pool.

Waits for all tasks to complete, then destroys all threads. Note that if the variable paused is set to true, then any tasks still in the queue will never be executed.

Definition at line 58 of file thread_pool.hpp.

Member Function Documentation

◆ create_threads()

void thread_pool::create_threads ( )
inlineprivate

Create the threads in the pool and assign a worker to each thread.

Definition at line 291 of file thread_pool.hpp.

◆ destroy_threads()

void thread_pool::destroy_threads ( )
inlineprivate

Destroy the threads in the pool by joining them.

Definition at line 300 of file thread_pool.hpp.

◆ get_tasks_queued()

ui64 thread_pool::get_tasks_queued ( ) const
inline

Get the number of tasks currently waiting in the queue to be executed by the threads.

Returns
The number of queued tasks.

Definition at line 73 of file thread_pool.hpp.

◆ get_tasks_running()

ui32 thread_pool::get_tasks_running ( ) const
inline

Get the number of tasks currently being executed by the threads.

Returns
The number of running tasks.

Definition at line 83 of file thread_pool.hpp.

◆ get_tasks_total()

ui32 thread_pool::get_tasks_total ( ) const
inline

Get the total number of unfinished tasks - either still in the queue, or running in a thread.

Returns
The total number of tasks.

Definition at line 92 of file thread_pool.hpp.

◆ get_thread_count()

ui32 thread_pool::get_thread_count ( ) const
inline

Get the number of threads in the pool.

Returns
The number of threads.

Definition at line 101 of file thread_pool.hpp.

◆ parallelize_loop()

template<typename T1 , typename T2 , typename F >
void thread_pool::parallelize_loop ( const T1 &  first_index,
const T2 &  index_after_last,
const F &  loop,
ui32  num_blocks = 0 
)
inline

Parallelize a loop by splitting it into blocks, submitting each block separately to the thread pool, and waiting for all blocks to finish executing.

The user supplies a loop function, which will be called once per block and should iterate over the block's range.

Template Parameters
T1The type of the first index in the loop. Should be a signed or unsigned integer.
T2The type of the index after the last index in the loop. Should be a signed or unsigned integer. If T1 is not the same as T2, a common type will be automatically inferred.
FThe type of the function to loop through.
Parameters
first_indexThe first index in the loop.
index_after_lastThe index after the last index in the loop. The loop will iterate from first_index to (index_after_last - 1) inclusive. In other words, it will be equivalent to "for (T i = first_index; i < index_after_last; i++)". Note that if first_index == index_after_last, the function will terminate without doing anything.
loopThe function to loop through. Will be called once per block. Should take exactly two arguments: the first index in the block and the index after the last index in the block. loop(start, end) should typically involve a loop of the form "for (T i = start; i < end; i++)".
num_blocksThe maximum number of blocks to split the loop into. The default is to use the number of threads in the pool.

Definition at line 117 of file thread_pool.hpp.

◆ pop_task()

bool thread_pool::pop_task ( std::function< void()> &  task)
inlineprivate

Try to pop a new task out of the queue.

Parameters
taskA reference to the task. Will be populated with a function if the queue is not empty.
Returns
true if a task was found, false if the queue is empty.

Definition at line 312 of file thread_pool.hpp.

◆ push_task() [1/2]

template<typename F >
void thread_pool::push_task ( const F &  task)
inline

Push a function with no arguments or return value into the task queue.

Template Parameters
FThe type of the function.
Parameters
taskThe function to push.

Definition at line 159 of file thread_pool.hpp.

◆ push_task() [2/2]

template<typename F , typename... A>
void thread_pool::push_task ( const F &  task,
const A &...  args 
)
inline

Push a function with arguments, but no return value, into the task queue.

The function is wrapped inside a lambda in order to hide the arguments, as the tasks in the queue must be of type std::function<void()>, so they cannot have any arguments or return value. If no arguments are provided, the other overload will be used, in order to avoid the (slight) overhead of using a lambda.

Template Parameters
FThe type of the function.
AThe types of the arguments.
Parameters
taskThe function to push.
argsThe arguments to pass to the function.

Definition at line 177 of file thread_pool.hpp.

◆ reset()

void thread_pool::reset ( const ui32 &  _thread_count = std::thread::hardware_concurrency())
inline

Reset the number of threads in the pool.

Waits for all currently running tasks to be completed, then destroys all threads in the pool and creates a new thread pool with the new number of threads. Any tasks that were waiting in the queue before the pool was reset will then be executed by the new threads. If the pool was paused before resetting it, the new pool will be paused as well.

Parameters
_thread_countThe number of threads to use. The default value is the total number of hardware threads available, as reported by the implementation. With a hyperthreaded CPU, this will be twice the number of CPU cores. If the argument is zero, the default value will be used instead.

Definition at line 186 of file thread_pool.hpp.

◆ sleep_or_yield()

void thread_pool::sleep_or_yield ( )
inlineprivate

Sleep for sleep_duration microseconds.

If that variable is set to zero, yield instead.

Definition at line 327 of file thread_pool.hpp.

◆ submit() [1/2]

template<typename F , typename... A, typename = std::enable_if_t<std::is_void_v<std::invoke_result_t<std::decay_t<F>, std::decay_t<A>...>>>>
std::future< bool > thread_pool::submit ( const F &  task,
const A &...  args 
)
inline

Submit a function with zero or more arguments and no return value into the task queue, and get an std::future<bool> that will be set to true upon completion of the task.

Template Parameters
FThe type of the function.
AThe types of the zero or more arguments to pass to the function.
Parameters
taskThe function to submit.
argsThe zero or more arguments to pass to the function.
Returns
A future to be used later to check if the function has finished its execution.

Definition at line 209 of file thread_pool.hpp.

◆ submit() [2/2]

template<typename F , typename... A, typename R = std::invoke_result_t<std::decay_t<F>, std::decay_t<A>...>, typename = std::enable_if_t<!std::is_void_v<R>>>
std::future< R > thread_pool::submit ( const F &  task,
const A &...  args 
)
inline

Submit a function with zero or more arguments and a return value into the task queue, and get a future for its eventual returned value.

Template Parameters
FThe type of the function.
AThe types of the zero or more arguments to pass to the function.
RThe return type of the function.
Parameters
taskThe function to submit.
argsThe zero or more arguments to pass to the function.
Returns
A future to be used later to obtain the function's returned value, waiting for it to finish its execution if needed.

Definition at line 237 of file thread_pool.hpp.

◆ wait_for_tasks()

void thread_pool::wait_for_tasks ( )
inline

Wait for tasks to be completed.

Normally, this function waits for all tasks, both those that are currently running in the threads and those that are still waiting in the queue. However, if the variable paused is set to true, this function only waits for the currently running tasks (otherwise it would wait forever). To wait for a specific task, use submit() instead, and call the wait() member function of the generated future.

Definition at line 256 of file thread_pool.hpp.

◆ worker()

void thread_pool::worker ( )
inlineprivate

A worker function to be assigned to each thread in the pool.

Continuously pops tasks out of the queue and executes them, as long as the atomic variable running is set to true.

Definition at line 337 of file thread_pool.hpp.

Member Data Documentation

◆ paused

std::atomic<bool> thread_pool::paused = false

An atomic variable indicating to the workers to pause.

When set to true, the workers temporarily stop popping new tasks out of the queue, although any tasks already executed will keep running until they are done. Set to false again to resume popping tasks.

Definition at line 276 of file thread_pool.hpp.

◆ queue_mutex

std::mutex thread_pool::queue_mutex = {}
mutableprivate

A mutex to synchronize access to the task queue by different threads.

Definition at line 356 of file thread_pool.hpp.

◆ running

std::atomic<bool> thread_pool::running = true
private

An atomic variable indicating to the workers to keep running.

When set to false, the workers permanently stop working.

Definition at line 361 of file thread_pool.hpp.

◆ sleep_duration

ui32 thread_pool::sleep_duration = 1000

The duration, in microseconds, that the worker function should sleep for when it cannot find any tasks in the queue.

If set to 0, then instead of sleeping, the worker function will execute std::this_thread::yield() if there are no tasks in the queue. The default value is 1000.

Definition at line 281 of file thread_pool.hpp.

◆ tasks

std::queue<std::function<void()> > thread_pool::tasks = {}
private

A queue of tasks to be executed by the threads.

Definition at line 366 of file thread_pool.hpp.

◆ tasks_total

std::atomic<ui32> thread_pool::tasks_total = 0
private

An atomic variable to keep track of the total number of unfinished tasks - either still in the queue, or running in a thread.

Definition at line 381 of file thread_pool.hpp.

◆ thread_count

ui32 thread_pool::thread_count
private

The number of threads in the pool.

Definition at line 371 of file thread_pool.hpp.

◆ threads

std::unique_ptr<std::thread[]> thread_pool::threads
private

A smart pointer to manage the memory allocated for the threads.

Definition at line 376 of file thread_pool.hpp.