Dynamic thread pool in C++ I have made a thread pool which will dynamically create threads based on how many you need.

#pragma once

#include &lt;thread&gt;
#include &lt;atomic&gt;
#include &lt;mutex&gt;
#include &lt;vector&gt;
#include &lt;functional&gt;
#include &lt;condition_variable&gt;

class thread_pool {
public:
    thread_pool() : stop(false), busy_workers(0) { }

    ~thread_pool() {
        stop = true;
        task_available.notify_all();
        for (auto&amp; worker : workers) {
            if (worker.joinable()) {
                worker.join();
            }
        }
    }

    void run_task(std::function&lt;void()&gt; task) {
        {
            std::lock_guard&lt;std::mutex&gt; task_lock{ task_mu };
            current_task = std::move(task);
        }

        {
            std::lock_guard&lt;std::mutex&gt; workers_lock{ workers_mu };
            if (workers.size() == busy_workers++) {
                workers.emplace_back(work);
                return;
            }
        }

        task_available.notify_one();
    }
private:
    std::atomic_bool stop;
    std::atomic_size_t busy_workers;
    std::vector&lt;std::thread&gt; workers;
    std::function&lt;void()&gt; current_task;
    std::condition_variable task_available;
    std::mutex task_mu;
    std::mutex workers_mu;

    std::function&lt;void()&gt; work = [&amp;]() {
        while (true) {
            std::unique_lock&lt;std::mutex&gt; task_lock{ task_mu };
            task_available.wait(task_lock, [&amp;]() { return current_task || stop; });

            if (!current_task &amp;&amp; stop) return;

            auto task = std::move(current_task);
            task_lock.unlock();

            task();
            busy_workers--;
        }
    };
};


The only problem I have with it is the threads will be destroyed before all the tasks have been completed, even though I join all the threads in the destructor.
Bugs

You have several race conditions:

There is no guarantee that a worker stays joinable between these two calls.

        if (worker.joinable()) {
            worker.join();


This is a multi-threaded environment you can not assume run_task is being called from only one thread. You must assume that it can be called from any number of threads.

    {
        std::lock_guard&lt;std::mutex&gt; task_lock{ task_mu };
        // There is no guarantee that `current_task` stays the same
        // between here and when you create a new worker thread.
        // As soon as the scope is exited another thread can enter
        // and overwrite `current_task` before the first thread
        // pushes `work` onto the thread queue or an existing worker
        // picks it up
        current_task = std::move(task);
    }


Design

Usually thread pools are created with a fixed number of threads. Jobs are added to a queue. When a thread becomes available it pulls a job from the queue or waits on a condition variable until a job is available.

I dislike the design of adding threads. As this could spawn lots of threads when there is a sudden high workload that are never released. Threads are expensive resources to create. Also creating many threads does not mean more parallelism (it just means more swapping). A machine usually has an upper bound of available parallelism allocating more threads than this is usually counter productive).