Welcome to a comprehensive guide on C++ Thread Pools! In this tutorial, we'll learn what thread pools are, why they're useful, and how to create and use them in your C++ projects.
By the end of this lesson, you'll be able to create efficient, scalable multi-threaded applications. Let's dive right in!
Thread pools are collections of worker threads that are managed by a thread pool manager. The manager handles the creation, scheduling, and termination of threads, allowing you to efficiently execute multiple tasks concurrently.
Think of a thread pool as a group of employees (threads) working in a factory (your program). The factory manager (thread pool manager) assigns tasks (work) to the employees (threads) to complete.
Let's create a simple thread pool using C++ and the Standard Template Library (STL).
#include <vector>
#include <queue>
#include <atomic>
#include <thread>
#include <functional>
#include <mutex>
#include <condition_variable>
class ThreadPool {
public:
ThreadPool(size_t numThreads);
~ThreadPool();
void enqueue(std::function<void()> task);
bool terminate();
private:
std::vector<std::thread> m_threads;
std::queue<std::function<void()>> m_tasks;
std::atomic_bool m_stop;
std::mutex m_queue_mutex;
std::condition_variable m_not_empty;
std::condition_variable m_not_full;
};š” Pro Tip: The std::function<void()> is a general purpose function object that can hold a function of any type that returns void and takes no arguments.
#include "ThreadPool.h"
ThreadPool::ThreadPool(size_t numThreads) : m_stop(false) {
m_threads.reserve(numThreads);
for (size_t i = 0; i < numThreads; ++i) {
m_threads.emplace_back(
[this] {
for (;;) {
std::function<void()> task;
{
std::unique_lock<std::mutex> lock(m_queue_mutex);
m_not_full.wait(lock, [this] { return m_stop || !m_tasks.empty(); });
if (m_stop && m_tasks.empty()) {
return;
}
task = std::move(m_tasks.front());
m_tasks.pop();
}
task();
}
}
);
}
}
ThreadPool::~ThreadPool() {
{
std::unique_lock<std::mutex> lock(m_queue_mutex);
m_stop = true;
}
m_not_empty.notify_all();
m_threads.clear();
}
void ThreadPool::enqueue(std::function<void()> task) {
std::unique_lock<std::mutex> lock(m_queue_mutex);
m_tasks.emplace(std::move(task));
m_not_full.notify_one();
lock.unlock();
m_not_empty.notify_one();
}
bool ThreadPool::terminate() {
std::unique_lock<std::mutex> lock(m_queue_mutex);
return m_stop;
}Now that we've created our thread pool, let's see how to use it!
#include "ThreadPool.h"
#include <iostream>
#include <vector>
#include <chrono>
void worker(int id) {
for (int i = 0; i < 10; ++i) {
std::cout << "Thread " << id << " doing some work...\n";
std::this_thread::sleep_for(std::chrono::milliseconds(100));
}
}
int main() {
ThreadPool pool(4);
std::vector<int> ids(4);
std::iota(ids.begin(), ids.end(), 0);
for (int id : ids) {
pool.enqueue(std::bind(worker, id));
}
pool.terminate();
pool.join();
return 0;
}This example creates a thread pool with 4 worker threads and enqueues 4 tasks (worker functions) to be executed concurrently.
Which C++ Standard Library is used for creating the thread pool in this tutorial?
That's it for our C++ Thread Pools tutorial! By now, you should have a good understanding of what thread pools are, why they're useful, and how to create and use them in your C++ projects. Happy coding! šÆš»š