quickpool  0.2.0
An easy-to-use, header-only work stealing thread pool in C++11
quickpool

build status Codacy Badge codecov License: MIT Documentation DOI

An easy-to-use, header-only work stealing thread pool in C++11.

Why quickpool?

  • no install: single-header library, C++11 compliant, no external dependencies.
  • fast: Uses a work stealing queue with lock-free pops.
  • user friendly: Dead simple API, including direct access to a global pool.
  • light weight: Less than 500 LOC including API documentation, comments, and whitespace.

Usage

Basic usage is demonstrated below. See the API documentation for more details.

Static access to a global pool

The easiest method is to use the quickpool::push(), quickpool::async(), and quickpool::wait() functions. They give access to a global thread pool that is only instantiated once with as many threads as there are cores.

#include "quickpool.hpp"
quickpool::push([] { /* some work */ });
quickpool::push([] { /* some work */ });
quickpool::wait(); // waits for all current jobs to finish

If a task also returns a result, use async(), which returns a std::future for the result.

auto f = quickpool::async([] { return 1 + 1; });
// do something else ...
auto result = f.get(); // waits until done and returns result

Both push() and async() can also be called with extra arguments passed to the function.

auto work = [] (const std::string& title, int i) {
std::cout << title << ": " << i << std::endl;
};
quickpool::push(work, "first title", 5);
quickpool::async(work, "other title", 99);

Local thread pool

A ThreadPool can also be set up manually, with an arbitrary number of threads. When the pool goes out of scope, all threads joined.

{
quickpool::ThreadPool pool(2); // thread pool with two threads
pool.push([] {});
pool.async([] {});
pool.wait();
}
// threads are joined

Task synchronization

In general, the pool may process the tasks in any order. Synchronization between tasks (e.g., one thread waiting intermediate results) must be done manually. Standard tools are std::mutex and std::condition_variable. quickpool exposes another synchronization primitive, TodoList, that may be useful.

std::vector<double> x(2); // shared resource
quickpool::TodoList todo_prod(2);
quickpool::TodoList todo_cons(2);
auto job_prod = [&](int i, double val) {
x.at(i) = val;
todo_prod.cross();
};
auto job_cons = [&](int i) {
todo_prod.wait(); // waits for all producers to finish
std::cout << x.at(i) << std::endl;
todo_cons.cross();
};
quickpool::push(job_prod, 0, 1.337); // writes x[0]
quickpool::push(job_prod, 1, 3.14); // writes x[1]
quickpool::push(job_cons, 0); // reads x[0]
quickpool::push(job_cons, 1); // reads x[1]
todo_cons.wait(); // waits for all consumers to finish

You can also add items on the fly using TodoList::add().

quickpool::async
auto async(Function &&f, Args &&... args) -> std::future< decltype(f(args...))>
executes a job asynchronously the global thread pool.
Definition: quickpool.hpp:450
quickpool::ThreadPool::wait
void wait()
waits for all jobs currently running on the global thread pool.
Definition: quickpool.hpp:411
quickpool::wait
void wait()
waits for all jobs currently running on the global thread pool.
Definition: quickpool.hpp:458
quickpool::TodoList
Todo list - a synchronization primitive.
Definition: quickpool.hpp:36
quickpool::ThreadPool::async
auto async(Function &&f, Args &&... args) -> std::future< decltype(f(args...))>
executes a job asynchronously the global thread pool.
Definition: quickpool.hpp:399
quickpool::ThreadPool
A work stealing thread pool.
Definition: quickpool.hpp:330
quickpool::ThreadPool::push
void push(Function &&f, Args &&... args)
pushes a job to the thread pool.
Definition: quickpool.hpp:384
quickpool::push
void push(Function &&f, Args &&... args)
Direct access to the global thread pool ----------------—.
Definition: quickpool.hpp:437