一、为什么使用线程池

大家都知道C++支持多线程开发,也就是支持多个任务并行运行,我们也知道线程的生命周期中包括创建、就绪、运行、阻塞、销毁等阶段,所以如果要执行的任务很多,每个任务都需要一个线程的话,那么频繁的创建、销毁线程会比较耗性能。

有了线程池就不用创建更多的线程来完成任务,它可以:

降低资源的消耗,通过重复利用已经创建的线程降低线程创建和销毁造成的消耗。

提高相应速度,当任务到达的时候,任务可以不需要等到线程创建就能立刻执行。

提高线程的可管理性,线程是稀缺资源,使用线程池可以统一的分配、调优和监控。

二、线程池的原理

通俗的讲,线程池就是一个线程集合,里面已经提前创建好了若干个线程,当需要线程的时候到线程集合里获取一个即可,这样省去了创建线程的时间,当然也省去了系统回收线程的时间,当线程池里的线程都被使用了后,只能阻塞等待了,等待获取线程池后被释放的线程。

当线程池提交一个任务到线程池后,执行流程如下:

线程池先判断核心线程池里面的线程是否都在执行任务。如果不是都在执行任务,则创建一个新的工作线程来执行任务。如果核心线程池中的线程都在执行任务,则判断工作队列是否已满。如果工作队列没有满,则将新提交的任务存储到这个工作队列中,如果工作队列满了,线程池则判断线程池的线程是否都处于工作状态。如果没有,则创建一个新的工作线程来执行任务。如果已经满了,则交给饱和策略来处理 ,也就是拒接策略。
一句话:管理一个任务队列,一个线程队列,然后每次去一个任务分配给一个线程去做,循环往复。

三、代码实现

有什么问题?线程池一般是要复用线程,所以如果是取一个task分配给某一个thread,执行完之后再重新分配,在语言层面这是基本不能实现的:C++的thread都是执行一个固定的task函数,执行完之后线程也就结束了。所以该如何实现task和thread的分配呢?

让每一个thread创建后,就去执行调度函数:循环获取task,然后执行。

这个循环该什么时候停止呢?

很简单,当线程池停止使用时,循环停止。

这样一来,就保证了thread函数的唯一性,而且复用线程执行task。

总结一下,我们的线程池的主要组成部分有二:

  • 任务队列(Task Queue)
  • 线程池(Thread Pool)

#ifndef THREAD_POOL_H
#define THREAD_POOL_H#include <vector>
#include <queue>
#include <memory>
#include <thread>
#include <mutex>
#include <condition_variable>
#include <future>
#include <functional>
#include <stdexcept>class ThreadPool {public:ThreadPool(size_t);                          //构造函数template<class F, class... Args>             //类模板auto enqueue(F&& f, Args&&... args)->std::future<decltype(func(args...))>;//任务入队~ThreadPool();                              //析构函数private:std::vector< std::thread > workers;            //线程队列,每个元素为一个Thread对象std::queue< std::function<void()> > tasks;     //任务队列,每个元素为一个函数对象    std::mutex queue_mutex;                        //互斥量std::condition_variable condition;             //条件变量bool stop;                                     //停止
};// 构造函数,把线程插入线程队列,插入时调用embrace_back(),用匿名函数lambda初始化Thread对象
inline ThreadPool::ThreadPool(size_t threads) : stop(false){for(size_t i = 0; i<threads; ++i)workers.emplace_back([this]{for(;;){// task是一个函数类型,从任务队列接收任务std::function<void()> task;  {//给互斥量加锁,锁对象生命周期结束后自动解锁std::unique_lock<std::mutex> lock(this->queue_mutex);//(1)当匿名函数返回false时才阻塞线程,阻塞时自动释放锁。//(2)当匿名函数返回true且受到通知时解阻塞,然后加锁。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>
auto ThreadPool::enqueue(F&& f, Args&&... args)->std::future<decltype(func(args...))>
{// 获取函数返回值类型        using return_type = decltype(func(args...));// 创建一个指向任务的智能指针auto task = std::make_shared< std::packaged_task<return_type()> >(std::bind(std::forward<F>(f), std::forward<Args>(args)...));std::future<return_type> res = task->get_future();{std::unique_lock<std::mutex> lock(queue_mutex);  //加锁if(stop)throw std::runtime_error("enqueue on stopped ThreadPool");tasks.emplace([task](){ (*task)(); });          //把任务加入队列}                                                   //自动解锁condition.notify_one();                             //通知条件变量,唤醒一个线程return res;
}// 析构函数,删除所有线程
inline ThreadPool::~ThreadPool()
{{std::unique_lock<std::mutex> lock(queue_mutex);stop = true;}condition.notify_all();for(std::thread &worker: workers)worker.join();
}#endif

使用

#include <iostream>
#include <chrono>
#include "ThreadPool.h"void func()
{std::this_thread::sleep_for(std::chrono::milliseconds(100));std::cout<<"worker thread ID:"<<std::this_thread::get_id()<<std::endl;
}int main()
{ThreadPool pool(4);while(1){pool.enqueue(func);}
}

打印

参考:

基于C++11实现线程池 - 知乎

C++实现线程池_蓬莱道人的博客-CSDN博客_c++ 线程池

C++实现线程池_晓枫寒叶的博客-CSDN博客_c++ 线程池
C++17future类+可变参模板实现线程池_刚入门的代码spa技师的博客-CSDN博客_c++17 可变参

C++实现一个线程池相关推荐

  1. java 手编线程池_死磕 java线程系列之自己动手写一个线程池

    欢迎关注我的公众号"彤哥读源码",查看更多源码系列文章, 与彤哥一起畅游源码的海洋. (手机横屏看源码更方便) 问题 (1)自己动手写一个线程池需要考虑哪些因素? (2)自己动手写 ...

  2. 随笔之如何实现一个线程池

    为什么80%的码农都做不了架构师?>>>    一 缘由:     最近因工作问题,需要实现一个简单的线程池,满足一下要求, 可伸缩,即一旦发现线程不够用,则可以动态增加线程.(至于 ...

  3. 一个线程池中的线程异常了,那么线程池会怎么处理这个线程?

    一个线程池中的线程异常了,那么线程池会怎么处理这个线程? 参考文章: (1)一个线程池中的线程异常了,那么线程池会怎么处理这个线程? (2)https://www.cnblogs.com/fangua ...

  4. 工作中如何使用线程池的?自己如何定义一个线程池?

    工作中如何使用线程池的?自己如何定义一个线程池? import java.util.concurrent.*;public class MyThreadPoolDemo {public static ...

  5. 设置iis网页服务器cpu占比,为什么iis的一个线程池占了100%cpu

    为什么iis的一个线程池占了快100%cpu, 这个站点是跑asp.net web api的,大多是数据库的操作. 当回收这个线程池后几分钟,cpu使用率就降下来了. 可是隔一天半天的再去服务器看,c ...

  6. 程序随笔——C++实现的一个线程池

    1.线程池简介 我们知道在线程池是一种多线程处理形式,处理过程中我们将相应的任务提交给线程池,线程池会分配对应的工作线程执行任务或存放在任务队列中,等待执行. 面向对象编程中,创建和销毁对象是需要消耗 ...

  7. 一个有趣的问题 : 如何设计一个线程池

    理解Java并发工具包线程池的设计 深度解读 java 线程池设计思想及源码实现 分布式锁unlock 问题产生原因分析: Step 1 :线程A先上同一个锁(Key)(20秒), 然后执行耗时业务, ...

  8. 【重难点】【JUC 05】线程池核心设计与实现、线程池使用了什么设计模式、要你设计的话,如何实现一个线程池

    [重难点][JUC 05]线程池核心设计与实现.线程池使用了什么设计模式.要你设计的话,如何实现一个线程池 文章目录 [重难点][JUC 05]线程池核心设计与实现.线程池使用了什么设计模式.要你设计 ...

  9. 面试官:如何评估一个线程池需要设置多少个线程

    作者 | 丁威       责编 | 欧阳姝黎 见字如面,我是威哥,一个从普通二本院校毕业,从未曾接触分布式.微服务.高并发到通过技术分享实现职场蜕变,成长为 RocketMQ 社区优秀布道师.大厂资 ...

  10. 如何实现一个“线程池”

    线程池里面包含了许多线程,可以供我们去使用,而避免了频繁的创建线程以及销毁线程,主要目的就是为了提高开发效率.那么我们如何实现一个自己的"线程池"呢 首先我们来看一下线程池的组成部 ...

最新文章

  1. 程序猿像妹子表白专用代码
  2. Java的标签--弱化的goto
  3. 学习dos批处理,再也不怕老板安排一些重复性高的工作了,几行代码就搞定!
  4. myeclipse不是eclipse,servlet 报错 HttpServlet cannot be resolved to a type
  5. 第五节:简单又强大的数据类型:any任意值
  6. Linux基础学习七:mysql的安装和配置教程
  7. 业务 T+1 T+2
  8. 数据结构:判断是否为同一棵二叉搜索树
  9. java关于替换文本输出的讲解_java替换文件中某一行文本的内容
  10. Sigar libsigar-amd64-linux.so
  11. php获取省市区区划代码,使用PHP解析行政区划代码
  12. java自定义对象集合排序
  13. 安卓不透明度和透明度
  14. 技术系列课回顾 | 网易云信变声技术之变调不变速算法
  15. 基于python的酒店管理系统_基于Web酒店管理系统的设计与实现
  16. 叶酸修饰的金星形纳米颗粒,Gold star shaped nanoparticles modified with folic acid
  17. 牛顿冷却定律:在用户标签提取上的应用
  18. matlab 黑白格子
  19. adb remount失败解决
  20. js 时间运算,时间加减

热门文章

  1. 微信公众平台素材编辑与自动回复图文教程
  2. MATLAB计算机器人工作空间【源码】
  3. vue实现浏览器桌面通知
  4. 上海好吃加好玩-详细分类版
  5. JDK8新特性02 Lambda表达式02_Lambda语法规则
  6. Android播放器实现横竖屏切换
  7. 安卓开发——视频播放器
  8. azkaban 项目依赖
  9. Laravel 学习笔记: 授权策略(Policy)
  10. html中div hover的用法,CSS: hover选择器的使用详解