读代码 - C++ 线程池

线程池原理架构

线程池包含三大核心模块:

  1. 任务队列(Task Queue):存储待处理的任务
  2. 固定数量的工作线程(Worker Threads):循环从队列中取任务执行
  3. 线程管理器(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();
}
  1. 条件变量等待机制
  • condition.wait(lock, predicate)会阻塞线程,直到predicate返回true
  • 等待期间会释放锁,允许其他线程操作任务队列
  • 被唤醒时会重新获取锁,确保线程安全
  1. 线程退出逻辑
  • 当stop为true且任务队列为空时,工作线程退出
  • 保证线程池停止前会完成所有已入队的任务
  1. 锁的范围控制
  • 只在操作任务队列时加锁,执行任务时释放锁
  • 避免因任务执行时间过长导致的锁竞争

创建任务对象

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会自动释放锁,无需手动解锁
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值