Thread pool in C++

C++11 through C++23 do not provide a standard thread pool in the STL. You either build one (queue + worker std::threads + std::mutex + std::condition_variable) or use a library. C++26 adds std::thread_pool. See What is Thread Pool?.

1. Manual thread pool (standard C++11+)

Classic pattern: N worker threads loop, pulling callables from a shared queue. This is how most production pools work when you cannot add a dependency.


#include <iostream>
#include <thread>
#include <vector>
#include <queue>
#include <mutex>
#include <condition_variable>
#include <functional>
#include <future>

class ThreadPool {
public:
    explicit ThreadPool(size_t n) : stop_(false) {
        for (size_t i = 0; i < n; ++i) {
            workers_.emplace_back([this] {
                while (true) {
                    std::function<void()> task;
                    {
                        std::unique_lock<std::mutex> lock(mutex_);
                        cv_.wait(lock, [this] { return stop_ || !tasks_.empty(); });
                        if (stop_ && tasks_.empty()) return;
                        task = std::move(tasks_.front());
                        tasks_.pop();
                    }
                    task();
                }
            });
        }
    }

    template <class F>
    void submit(F&& f) {
        {
            std::lock_guard<std::mutex> lock(mutex_);
            tasks_.emplace(std::forward<F>(f));
        }
        cv_.notify_one();
    }

    ~ThreadPool() {
        {
            std::lock_guard<std::mutex> lock(mutex_);
            stop_ = true;
        }
        cv_.notify_all();
        for (auto& t : workers_) t.join();
    }

private:
    std::vector<std::thread> workers_;
    std::queue<std::function<void()>> tasks_;
    std::mutex mutex_;
    std::condition_variable cv_;
    bool stop_;
};

int main() {
    ThreadPool pool(4);
    for (int i = 0; i < 10; ++i) {
        pool.submit([i] { std::cout << "task " << i << "\n"; });
    }
    // pool destructor waits for workers
}
      

2. std::async — not a true pool

std::async(std::launch::async, ...) may spawn a new thread per call — no reuse, no queue limit. Fine for a few tasks; not a thread pool substitute at scale.


#include <future>
#include <iostream>

int main() {
    auto fut = std::async(std::launch::async, [] {
        return 42;
    });
    std::cout << fut.get() << "\n";
}
      

3. C++26 — std::thread_pool

The C++26 standard adds a built-in thread pool (P0443). Syntax may vary slightly by compiler; check your toolchain.


// C++26 (when available)
#include <thread_pool>
#include <iostream>

int main() {
    std::thread_pool pool(4);
    pool.submit([] { std::cout << "task on pool\n"; });
    pool.wait();   // wait for submitted work
}
      

4. Third-party libraries

When you need a production pool today without writing your own:


// BS::thread_pool example (header-only library)
#include "BS_thread_pool.hpp"

int main() {
    BS::thread_pool pool(4);
    for (int i = 0; i < 10; ++i)
        pool.submit_task([i] { /* work */ });
    pool.wait();
}