The class supplies a simple thread pool that uses the thread class from C++11.When I schedule the job, I pass two std::function<void()> objects that the pool will execute: the first function is the real job, the second is executed to send some sort of notification that the job has been done.I'm not sure if I should merge the two std::function in one and let the user decide if to send the notification or not.Additionally, not so sure about the naming executeJob vs scheduleJob.The checking of maxJobsInQueue is not there yet, but when done I cannot decide between throwing exception when the queue is full vs blocking in executeJob until the job can be inserted.Any other improvement you can suggest?evr_threadpool.h:#ifndef EVR_THREADPOOL_H#define EVR_THREADPOOL_H#include <thread>#include <vector>#include <list>#include <memory>#include <functional>#include <mutex>#include <condition_variable>#include <atomic>#include <tuple>/** * @brief The ThreadPool class launches a pre-defined number of threads and *        keeps them ready to execute jobs. * * Each job is composed by two functions (std::function<void()>): * the two functions are executed one after another, and the second one *  usually sends some sort of notification that the job has been done. * */class ThreadPool{public:    /**     * @brief Initializes the pool and launches the threads.     *     * @param numThreads number of threads to launch.     * @param maxJobsInQueue     */    ThreadPool(size_t numThreads, size_t maxJobsInQueue);    /**     * @brief Destructors     *     * Sends a terminate signal to the threads and waits for     *  their termination.     *     * The threads will complete the currently running job     *  before checking for the terminate flag.     */    virtual ~ThreadPool();    /**     * @brief Schedule a job for execution.     *     * The first available thread will pick up the job and run it.     *     * @param job             a function that executes the job. It is called     *                         in the thread that pickec it up     * @param notificationJob a function that usually sends a notification     *                         that the job has been executed. it is executed     *                         immediately after the first function in the same     *                         thread that ran the first function     */    void executeJob(std::function<void()> job, std::function<void()> notificationJob);private:    /**     * @brief Function executed in each worker thread.     *     * Runs until the termination flag m_bTerminate is set to true     *  in the class destructor     */    void loop();    /**     * @brief Returns the next job scheduled for execution.     *     * The function blocks if the list of scheduled jobs is empty until     *  a new job is scheduled or until m_bTerminate is set to true     *  by the class destructor, in which case it throws Terminated.     *     * When a valid job is found it is removed from the queue and     *  returned to the caller.     *     * @return the next job to execute     */    std::pair<std::function<void()>, std::function<void()> > getNextJob();    /**     * @brief Contains the running working threads (workers).     */    std::vector<std::unique_ptr<std::thread> > m_workers;    /**     * @brief Queue of jobs scheduled for execution and not yet executed.     */    std::list<std::pair<std::function<void()>, std::function<void()> > > m_jobs;    /**     * @brief Mutex used to access the queue of scheduled jobs (m_jobs).     */    std::mutex m_lockJobsList;    /**     * @brief Condition variable used to notify that a new job has been     *        inserted in the queue (m_jobs).     */    std::condition_variable m_notifyJob;    /**     * @brief This flag is set to true by the class destructor to signal     *         the worker threads that they have to terminate.     */    std::atomic<bool> m_bTerminate;    /**     * @brief This exception is thrown by getNextJob() when the flag     *         m_bTerminate has been set.     */    class Terminated: public std::runtime_error    {    public:        Terminated(const std::string& what): std::runtime_error(what) {}    };};#endif // EVR_THREADPOOL_Hevr_threadpool.cpp#include evr_threadpool.h/* * Constructor *************/ThreadPool::ThreadPool(size_t numThreads, size_t maxJobsInQueue):    m_workers(numThreads), m_bTerminate(false){    for(std::unique_ptr<std::thread>& worker: m_workers)    {        worker.reset(new std::thread(&ThreadPool::loop, this));    }}/* * Destructor ************/ThreadPool::~ThreadPool(){    {        std::unique_lock<std::mutex> lockList(m_lockJobsList);        m_bTerminate = true;        m_notifyJob.notify_all();    }    for(std::unique_ptr<std::thread>& worker: m_workers)    {        worker->join();    }}/* * Schedule a job ****************/void ThreadPool::executeJob(std::function<void()> job, std::function<void()> notificationJob){    std::unique_lock<std::mutex> lockList(m_lockJobsList);    m_jobs.push_back(std::pair<std::function<void()>, std::function<void()> >(job, notificationJob));    m_notifyJob.notify_one();}/* * Retrieve the next job to execute **********************************/std::pair<std::function<void()>, std::function<void()> > ThreadPool::getNextJob(){    std::unique_lock<std::mutex> lockList(m_lockJobsList);    while(!m_bTerminate)    {        if(!m_jobs.empty())        {            std::pair<std::function<void()>, std::function<void()> > job = m_jobs.front();            m_jobs.pop_front();            return job;        }        m_notifyJob.wait(lockList);    }    throw Terminated(Thread terminated);}/* * Function executed by each worker **********************************/void ThreadPool::loop(){    try    {        for(;;)        {            std::pair<std::function<void()>, std::function<void()> > job = getNextJob();            job.first();            job.second();        }    }    catch(Terminated& e)    {    }}
Thread pool on C++11
c++;multithreading;c++11
A few thoughts:If I understand this correctly, if one of your job functions throws an exception, it ends processing for your entire thread pool; Threads that are running (but did not throw an exception) will keep running, but the ThreadPool object may be deleted as part of stack unwinding. Consider adding another callback to your executeJob similar to std::function<void(/* err details here */)> on_error.Your executeJob function doesn't actually execute the job - I would rename it to enqueueJob or addJob or similar.Your Terminated class is a runtime error specialization, but your documentation (or anything in the code really) does not explain why it is a runtime error to have an empty queue.I assume you use this to stop, not to signal an error state. This may not look like a big thing, but a runtime_error (or specialization) should actually signal an error during the runtime of your applicaition).For example, the Visual Studio debugger can be set to break automatically when a runtime_error is thrown in the application. Using this implementation would screw up debugging without you meaning to.Consider either using a second condition variable (or similar) instead of throwing an exception, or adding docs to the source saying Terminate is a runtime_error specialization because having no jobs to run is an error in this and this case.