Thread Pool Example

// ThreadPool.hpp

class ThreadPool {
private:
  // Thread Pool List
  std::vector<std::thread> workers;
  // Task Queue
  std::queue<std::function<void()>> tasks;
  // Stop flag
  bool stop = false;

  std::mutex mtx; // Lock variable
  std::condition_variable cv; // hold the thread variable

private:
  void worker_loop();

public:
  ThreadPool(int thread_num);
  template <class Func, class... Args>
  void submit(Func &&func, Args &&...args) {
    auto task =
        std::bind(std::forward<Func>(func), std::forward<Args>(args)...);
    {
      std::lock_guard<std::mutex> lock(mtx);
      if (stop) {
        return;
      }
      tasks.emplace(std::move(task));
    }
    cv.notify_one();
  }
  ~ThreadPool();
};
// ThreadPool.cpp
#include "./ThreadPool.hpp"
#include "iostream"

std::mutex cout_mtx;

ThreadPool::ThreadPool(int thread_num) {
  workers.reserve(thread_num);
  for (int i = 0; i < thread_num; i++) {
    workers.emplace_back(&ThreadPool::worker_loop, this);
  }
}

ThreadPool::~ThreadPool() {
  {
    std::lock_guard<std::mutex> lock(mtx);
    stop = true;
  }
  cv.notify_all();

  for (auto &t : workers) {
    if (t.joinable()) {
      t.join();
    }
  }
}

void ThreadPool::worker_loop() {
  {
    std::lock_guard<std::mutex> lock(mtx);
    std::cout << "Thread start ID:" << std::this_thread::get_id() << "\n";
  }

  while (true) {
    std::function<void()> task;
    {
      std::unique_lock<std::mutex> lock(mtx);
      cv.wait(lock, [this] { return stop || !tasks.empty(); });

      if (stop && tasks.empty()) {
        return;
      }

      task = std::move(tasks.front());
      tasks.pop();
    }

    task();
  }
}

void delayedTask(int id, int delay_ms) {
  {
    std::lock_guard<std::mutex> lock(cout_mtx);
    std::cout << "[Task " << id << "] start on thread "
              << std::this_thread::get_id() << ", delay = " << delay_ms
              << "ms\n";
  }
  std::this_thread::sleep_for(std::chrono::milliseconds(delay_ms));

  {
    std::lock_guard<std::mutex> lock(cout_mtx);
    std::cout << "[Task " << id << "] end   on thread "
              << std::this_thread::get_id() << "\n";
  }
}
int main() {
  ThreadPool pool(3);
  pool.submit(delayedTask, 1, 1000);
  pool.submit(delayedTask, 2, 2000);
  pool.submit(delayedTask, 3, 3000);
  pool.submit(delayedTask, 4, 4000);
  pool.submit(delayedTask, 5, 5000);
  pool.submit(delayedTask, 6, 6000);
}

// Possible output
/**
 *
Thread start ID:131225538787008
[Task 1] start on thread 131225538787008, delay = 1000ms
Thread start ID:131225522001600
[Task 2] start on thread 131225522001600, delay = 2000ms
Thread start ID:131225530394304
[Task 3] start on thread 131225530394304, delay = 3000ms
[Task 1] end   on thread 131225538787008
[Task 4] start on thread 131225538787008, delay = 4000ms
[Task 2] end   on thread 131225522001600
[Task 5] start on thread 131225522001600, delay = 5000ms
[Task 3] end   on thread 131225530394304
[Task 6] start on thread 131225530394304, delay = 6000ms
[Task 4] end   on thread 131225538787008
[Task 5] end   on thread 131225522001600
[Task 6] end   on thread 131225530394304
 */