C++项目实战--线程池代码讲解

先上代码:

#ifndef THREADPOOL_H
#define THREADPOOL_H

#include <mutex>
#include <condition_variable>
#include <queue>
#include <thread>
#include <functional>
class ThreadPool {
public:
    explicit ThreadPool(size_t threadCount = 8): pool_(std::make_shared<Pool>()) {
            assert(threadCount > 0);    //断言
            for(size_t i = 0; i < threadCount; i++) {  //创建 threadCount个工作线程
                std::thread([pool = pool_] {    //创建线程  将pool_ 赋值给 pool
                    std::unique_lock<std::mutex> locker(pool->mtx); //创建一个锁
                    //工作线程的逻辑
                    while(true) {       //不断的去向工作队列请求
                        if(!pool->tasks.empty()) { //如果工作队列不为空
                            auto task = std::move(pool->tasks.front());//获取一个任务
                            pool->tasks.pop();  //队列弹出已经被获取的任务
                            locker.unlock();    //解锁
                            task();             //处理任务
                            locker.lock();      //上锁
                        } 
                        else if(pool->isClosed) break; //如果工作队列为空,并且池子已经关闭,退出
                        else pool->cond.wait(locker);  //如果工作队列为空并且线程池没有关闭,则设置该工作线程阻塞等待 条件变量-1
                    }
                }).detach();    //设置线程分离
            }
    }

    ThreadPool() = default;         //采用默认的构造函数

    ThreadPool(ThreadPool&&) = default;     //采用默认的拷贝构造函数
    
    ~ThreadPool() {
        if(static_cast<bool>(pool_)) {
            {
                std::lock_guard<std::mutex> locker(pool_->mtx);     //创建一个锁
                pool_->isClosed = true;             //关闭线程池
            }       
            pool_->cond.notify_all();               //唤醒线程池中所以的线程,线程最终就会进入  else if(pool->isClosed) break; 逻辑,最后退出
        }
    }

    template<class F>
    void AddTask(F&& task) {    //向线程池中添加任务
        {
            std::lock_guard<std::mutex> locker(pool_->mtx);  //创建一个锁
            pool_->tasks.emplace(std::forward<F>(task));     //向工作队列中插入一个任务
        }
        pool_->cond.notify_one();   //唤醒一个线程  条件变量+1 
    }

private:
    struct Pool {           
        std::mutex mtx;                 //互斥锁    
        std::condition_variable cond;  //条件变量
        bool isClosed;                 //线程池是否关闭
        std::queue<std::function<void()>> tasks; //工作队列
    };
    std::shared_ptr<Pool> pool_;        //定义一个 Pool指针
};


#endif //THREADPOOL_H

这段代码是通过c++14写的,我们主要看逻辑关系和实现的原理。

一般线程池的实现的模型:消费者生产者

既然是生产者消费者模型,就设计到了线程同步的问题。。。。

线程同步问题:互斥量,条件变量

我们把代码分为三个部分来看:

这一部分是线程池所需要的一些条件

将互斥锁、条件变量、线程池是否关闭的状态、工作队列封装到一起,然后通过智能指针(共享的)来操作管理这些条件

 向生产者向工作队列中添加任务(主线程去完成),并设置唤醒

explicit:防止构造函数出现隐式转换。比如 A  a = 8,这样是不允许的

通过while不断让工作线程的请求工作队列,如果工作队列不为空,则获取一个任务并处理。如果工作队列为空并且线程池已经关闭,则直接跳出。如果工作队列不为空并且线程池没有关闭,则阻塞等待被唤醒。

使用线程池可以减少线程的销毁,而且如果不使用线程池的话,来一个客户端就创建一个线程。比如有1000,这样线程的创建、线程之间的调度也会耗费很多的系统资源,所以采用线程池使程序的效率更高。 线程池就是项目启动的时候,就先把线程池准备好。

猜你喜欢

转载自blog.csdn.net/weixin_46120107/article/details/126652875