Skip to content

Threading

Romain Milbert edited this page Apr 21, 2026 · 10 revisions

Thread pool

A thread pool object can be used to execute actions on different threads. The concept of a thread pool is a collection of threads running endlessly, to which you can give actions to execute.

Warning

Do keep in mind that an action pushed to a thread pool is not blocking, meaning that concurrency issues can arise if care is not taken.

#include <RaZ/Utils/ThreadPool.hpp>

// A thread pool can be created by giving a specific number of threads, or nothing to get the default amount
Raz::ThreadPool threadPool; // Equivalent to Raz::ThreadPool threadPool(Raz::Threading::getSystemThreadCount());

// Actions can be pushed to the pool using the addAction() member function:

int i = 0;
threadPool.addAction([&i] () { i = 42; });
// The variable i will be equal to 42 anytime. However, since it is asynchronous, there is no guarantee
//  that the value will immediately be set, so using it directly after this call may provoke a data race

threadPool.addAction([&i] () { i = 3; });
// For the same reason, this other function is dangerous: nothing requires that i will be set first to 42,
//  then to 3. Specific action must be taken, like setting up a mutex or making i an atomic variable

A "default" thread pool, created with the default thread count & used for other parallel algorithms, is available through Raz::Threading::getDefaultThreadPool().

Parallelization

Actions can be performed by multiple threads, either individually or over separate ranges of any collection:

#include <RaZ/Utils/Threading.hpp>

// Executing an action on 4 different threads. Be careful about data concurrency!
Raz::Threading::parallelize([] () { ... }, 4);

std::array<int, 5> values = { 1, 2, 3, 4, 5 };

// Incrementing all elements in the array on multiple threads
// No overlap between values is created, making this function thread safe for the given collection
// This of course works even when giving a number of threads not divisible by the collection's size
Raz::Threading::parallelize(0, values.size(), [&values] (const Raz::Threading::IndexRange& range) {
    for (std::size_t i = range.beginIndex; i < range.endIndex; ++i)
        ++values[i];
}, Raz::Threading::getDefaultThreadPool(), 4); // Starting 4 tasks in the default thread pool

// Declaring a specific thread pool with 2 threads, which will be named "Incrementer 1"
// and "Incrementer 2"
Raz::ThreadPool threadPool(2, "Incrementer");

// You can also give two iterators to iterate over. In the example below, 'values' could have
//   been strictly replaced by 'values.begin(), values.end()'
// Strongly consider the use of 'const auto& range' instead of specifying the whole type
Raz::Threading::parallelize(values, [] (const Raz::Threading::IterRange<std::array<int, 5>::iterator>& range) {
    for (int& val : range)
        ++val;
}, threadPool, 4); // Using our custom thread pool to start tasks into

These functions block the main thread before exiting, waiting for all threads to finish their tasks, so that you don't have to.

Clone this wiki locally