先上代码:
- #ifndef THREADPOOL_H
- #define THREADPOOL_H
-
- #include
- #include
- #include
- #include
- #include
- class ThreadPool {
- public:
- explicit ThreadPool(size_t threadCount = 8): pool_(std::make_shared
()) { - assert(threadCount > 0); //断言
- for(size_t i = 0; i < threadCount; i++) { //创建 threadCount个工作线程
- std::thread([pool = pool_] { //创建线程 将pool_ 赋值给 pool
- std::unique_lock
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
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
locker(pool_->mtx) ; //创建一个锁 - pool_->tasks.emplace(std::forward
(task)); //向工作队列中插入一个任务 - }
- pool_->cond.notify_one(); //唤醒一个线程 条件变量+1
- }
-
- private:
- struct Pool {
- std::mutex mtx; //互斥锁
- std::condition_variable cond; //条件变量
- bool isClosed; //线程池是否关闭
- std::queue
void()>> tasks; //工作队列 - };
- std::shared_ptr
pool_; //定义一个 Pool指针 - };
-
-
- #endif //THREADPOOL_H
这段代码是通过c++14写的,我们主要看逻辑关系和实现的原理。
一般线程池的实现的模型:消费者生产者
既然是生产者消费者模型,就设计到了线程同步的问题。。。。
线程同步问题:互斥量,条件变量
我们把代码分为三个部分来看:
这一部分是线程池所需要的一些条件
将互斥锁、条件变量、线程池是否关闭的状态、工作队列封装到一起,然后通过智能指针(共享的)来操作管理这些条件
向生产者向工作队列中添加任务(主线程去完成),并设置唤醒
explicit:防止构造函数出现隐式转换。比如 A a = 8,这样是不允许的
通过while不断让工作线程的请求工作队列,如果工作队列不为空,则获取一个任务并处理。如果工作队列为空并且线程池已经关闭,则直接跳出。如果工作队列不为空并且线程池没有关闭,则阻塞等待被唤醒。
使用线程池可以减少线程的销毁,而且如果不使用线程池的话,来一个客户端就创建一个线程。比如有1000,这样线程的创建、线程之间的调度也会耗费很多的系统资源,所以采用线程池使程序的效率更高。 线程池就是项目启动的时候,就先把线程池准备好。