线程池原理架构
线程池包含三大核心模块:
- 任务队列(Task Queue):存储待处理的任务
- 固定数量的工作线程(Worker Threads):循环从队列中取任务执行
- 线程管理器(ThreadPool):负责启动、停止线程和任务调度
示例代码
#include <iostream>
#include <vector>
#include <queue>
#include <thread>
#include <mutex>
#include <condition_variable>
#include <functional>
#include <atomic>
class ThreadPool {
public:
explicit ThreadPool(size_t num_threads) : stop(false) {
// 创建固定数量的工作线程
for (size_t i = 0; i < num_threads; ++i) {
workers.emplace_back([this]() {
while (true) {
std::function<void()> task;
{ // 加锁取任务
std::unique_lock<std::mutex> lock(this->queue_mutex);
this->condition.wait(lock, [this]() {
return this->stop || !this->tasks.empty();
});
if (this->stop && this->tasks.empty())
return;
task = std::move(this->tasks.front());
this->tasks.pop();
}
// 执行任务
task();
}
});
}
}
// 提交任务
template<class F, class... Args>
void enqueue(F&& f, Args&&... args) {
auto task = std::bind(std::forward<F>(f), std::forward<Args>(args)...);
{ // 线程安全加入任务队列
std::unique_lock<std::mutex> lock(queue_mutex);
if (stop)
throw std::runtime_error("enqueue on stopped ThreadPool");
tasks.emplace(task);
}
condition.notify_one(); // 唤醒一个线程
}
// 线程池销毁,等待线程退出
~ThreadPool() {
{
std::unique_lock<std::mutex> lock(queue_mutex);
stop = true;
}
condition.notify_all();
for (std::thread &worker : workers) {
if (worker.joinable())
worker.join();
}
}
private:
std::vector<std::thread> workers; // 工作线程集合
std::queue<std::function<void()>> tasks; // 任务队列
std::mutex queue_mutex; // 队列互斥锁
std::condition_variable condition; // 条件变量控制线程唤醒
std::atomic<bool> stop; // 停止标志
};
// 示例任务
void example_task(int n) {
std::cout << "Task " << n << " is running in thread "
<< std::this_thread::get_id() << std::endl;
}
int main() {
ThreadPool pool(4); // 创建包含4个线程的线程池
// 提交10个任务
for (int i = 0; i < 10; ++i) {
pool.enqueue(example_task, i);
}
std::this_thread::sleep_for(std::chrono::seconds(2)); // 模拟主线程其他操作
std::cout << "Main thread exits." << std::endl;
return0;
}
构造函数接收一个num_threads参数(工作线程数量),并创建对应数量的工作线程,这些线程会循环等待并执行任务队列中的任务。
初始化列表
explicit ThreadPool(size_t num_threads) : stop(false)
explicit关键字防止隐式类型转换,确保只能显式创建 ThreadPool 对象
初始化stop标志为false(线程池初始状态为运行中)
创建工作线程
for (size_t i = 0; i < num_threads; ++i) {
workers.emplace_back([this]() { ... });
}
循环创建num_threads个工作线程,存储在workers容器中(通常是std::vectorstd::thread)
每个线程执行一个 lambda 函数(工作线程的主逻辑)
工作线程主逻辑
while (true) {
std::function<void()> task;
{
// 加锁获取任务
std::unique_lock<std::mutex> lock(this->queue_mutex);
// 等待条件:线程池停止 或 任务队列非空
this->condition.wait(lock, [this]() {
return this->stop || !this->tasks.empty();
});
// 退出条件:线程池已停止且任务队列为空
if (this->stop && this->tasks.empty())
return;
// 从队列取任务(转移所有权)
task = std::move(this->tasks.front());
this->tasks.pop();
}
// 执行任务(解锁后执行,避免长时间持有锁)
task();
}
- 条件变量等待机制
- condition.wait(lock, predicate)会阻塞线程,直到predicate返回true
- 等待期间会释放锁,允许其他线程操作任务队列
- 被唤醒时会重新获取锁,确保线程安全
- 线程退出逻辑
- 当stop为true且任务队列为空时,工作线程退出
- 保证线程池停止前会完成所有已入队的任务
- 锁的范围控制
- 只在操作任务队列时加锁,执行任务时释放锁
- 避免因任务执行时间过长导致的锁竞争
创建任务对象
auto task = std::bind(std::forward<F>(f), std::forward<Args>(args)...);
- 这行代码使用std::bind将函数f与其参数args绑定,创建一个可调用的任务对象。std::forward用于完美转发,保持参数的左值 / 右值特性,提高效率。
- 最终task是一个std::function<void()>类型的可调用对象。
- 从函数签名void enqueue(F&& f, Args&&… args)可以看出,这是一个可变参数模板函数:
- F&& f:表示要执行的函数(可以是普通函数、lambda、函数对象等)
- Args&&… args:表示函数f的参数列表(可变参数)
{
std::unique_lock<std::mutex> lock(queue_mutex);
if (stop)
throw std::runtime_error("enqueue on stopped ThreadPool");
tasks.emplace(task);
}
- 通过std::unique_lock对任务队列加锁,确保多线程环境下的入队操作安全
- 检查线程池是否已停止(stop为true),如果已停止则抛出异常
- 使用tasks.emplace(task)将任务添加到队列(通常tasks是std::queue<std::function<void()>>类型)
- 代码块结束后,unique_lock自动释放锁,避免长时间持有锁
condition.notify_one();
- 调用条件变量的notify_one()方法,唤醒一个正在等待的工作线程
- 被唤醒的线程会检查任务队列,发现新任务后会立即取出执行
线程安全的队列操作:
{ // 线程安全加入任务队列
std::unique_lock<std::mutex> lock(queue_mutex);
if (stop)
throw std::runtime_error("enqueue on stopped ThreadPool");
tasks.emplace(task);
}
- 使用std::unique_lock加锁queue_mutex,确保对任务队列的操作是线程安全的
- 检查线程池是否已停止(stop标志),如果已停止则抛出异常
- 通过tasks.emplace(task)将任务添加到任务队列中(tasks通常是std::queue类型)
- 当离开这个代码块时,unique_lock会自动释放锁,无需手动解锁
1588

被折叠的 条评论
为什么被折叠?



