前面我们实现了CallBack类,实现了对任意可调用对象的封装,且统一了调用接口。

现在利用CallBack类,我们来实现一个线程池,我们的线程池包含:

1. 状态机, 用于控制和管理线程池的运行、停止

2. 任务队列, std::queue< std::unique_ptr< Base::Closure > > tasks;   维护了一个queue来保存、添加、删除需要执行的任务(用户定义回调函数,封装成CallBack统一接口)

3. 存放运行线程的容器, std::vector< std::thread > workers; 每次新建一个线程执行一个task,并将该线程放入vector里,便于对线程进行统一的管理

4. 同步锁和条件变量:

std::mutex worker_mutex; // 用于多个线程访问workers时的同步
       std::mutex queue_mutex; // 用于多个线程访问tasks时的同步
       std::condition_variable condition; // 用于添加任务线程与执行线程之间的协作,以及用于状态机改变时的协作等

#include <functional>
#include<mutex>
#include<vector>
#include<queue>
#include<condition_variable>
#include<thread>
#include<memory>
#include<future>
#include<iostream> namespace Base { class CallBack; typedef CallBack Closure; class CallBack {
public:
template<class F, class... Args>
CallBack(F&& f, Args&&... args) {
task = std::bind(std::forward<F>(f), std::forward<Args>(args)...);
} void Run() {
task();
} virtual ~CallBack() {} private:
std::function<void()> task;
}; template<class F, class... Args>
CallBack* NewCallBack(F&& f, Args&&... args) {
return new CallBack(f, args...);
} } // namespace Base class FixedThreadPool{
public:
enum State{
IDLE,
RUNNING,
STOP,
}; FixedThreadPool();
FixedThreadPool( size_t );
FixedThreadPool ( const FixedThreadPool& ) = delete ;
FixedThreadPool& operator=( const FixedThreadPool& ) = delete ; void SetPoolSize( size_t );
size_t Size() const; void AddTask( Base::Closure* task );
~FixedThreadPool(); void AwaitTermination();
void Start();
void Stop(); private:
void ThreadWorker(); std::vector< std::thread > workers; // 用于放置运行线程 std::queue< std::unique_ptr< Base::Closure > > tasks; // 任务队列 std::mutex worker_mutex;
std::mutex queue_mutex;
std::condition_variable condition; State state_;
unsigned int thread_size_;
}; FixedThreadPool::FixedThreadPool():
state_(IDLE),
thread_size_(4)
{
} FixedThreadPool::FixedThreadPool(size_t num_threads):
state_(IDLE),
thread_size_(num_threads)
{
} void FixedThreadPool::SetPoolSize(size_t size) {
thread_size_ = size;
} size_t FixedThreadPool::Size() const {
return thread_size_;
} // Destructor joins all threads
FixedThreadPool::~FixedThreadPool() {
for(std::thread &worker: workers) {
if (worker.joinable()) {
worker.join();
}
}
} // 根据线程池的size(也就是线程数),与任务数量,创建恰当数量的线程,分别从tasks队列里取出任务执行.
// 并把这些线程都放进workers容器里,方便统一管理
void FixedThreadPool::Start() {
{
std::unique_lock<std::mutex> lock(queue_mutex);
state_ = RUNNING;
unsigned int num_working_threads_ =
thread_size_ < tasks.size()? thread_size_ : tasks.size();
for (unsigned int i = workers.size(); i < num_working_threads_; i++) {
workers.emplace_back(std::thread(&FixedThreadPool::ThreadWorker, this));
}
}
condition.notify_all();
} // Thread worker 从tasks队列里取一个任务(回调函数),执行
void FixedThreadPool::ThreadWorker() {
Base::Closure* task;
while (1) {
{
std::unique_lock<std::mutex> lock(queue_mutex);
condition.wait(lock,
[this] { return state_ == STOP || !tasks.empty(); }); // state_==STOP && tasks==empty , state_!=STOP && tasks!=empty, state_==STOP && tasks != empty 继续往下执>行; state_ != STOP && tasks==empty 阻塞等待
if (state_ == STOP && tasks.empty()) {
return;
}
task = (tasks.front()).release();
tasks.pop();
}
task->Run();
}
} // Add new work item to the pool
// 如果当前工作线程数小于线程池的size, 就新建一个线程,从tasks队列取出一个任务并执行,并将该线程放入workers容器
// 然后将该任务task 放进tasks系列, 最后notify_one, 唤醒一个执行线程去执行任务
void FixedThreadPool::AddTask(Base::Closure* task) {
{
std::unique_lock<std::mutex> lock(worker_mutex);
if (state_ == RUNNING && workers.size() < thread_size_) {
workers.emplace_back(std::thread(&FixedThreadPool::ThreadWorker, this));
}
} {
std::unique_lock<std::mutex> lock(queue_mutex);
if (state_ == STOP) {
throw std::runtime_error("enqueue on stopped ThreadPool");
}
tasks.emplace(std::unique_ptr<Base::Closure>(task));
}
condition.notify_one();
} // Blocks and wait for all previously submitted tasks to be completed.
void FixedThreadPool::AwaitTermination() {
condition.notify_all();
for(std::thread &worker: workers) {
if (worker.joinable()) {
worker.join();
}
}
} // Shut down the threadpool. This method does not wait for previously submitted
// tasks to be completed.
void FixedThreadPool::Stop() {
{
std::unique_lock<std::mutex> lock(queue_mutex);
state_ = STOP;
}
condition.notify_all();
} } int func( int a ){
std::this_thread::sleep_for( std::chrono::seconds(100) );
printf(" a = %d\n", a);
return 0;
} void testThreadpool(){
FixedThreadPool pool(4);
pool.Start();
pool.AddTask(Base::NewCallBack(func, 3));
/*for(int i = 0; i < 10; ++i) {
pool.AddTask(new Base::CallBack([i] {
std::cout << "hello " << i << std::endl;
std::this_thread::sleep_for(std::chrono::seconds(1));
std::cout << "world " << i << std::endl;
return i*i;
}));
}*/ pool.Stop();
pool.AwaitTermination();
std::cout << "All tasks complted." << std::endl; } int main(){
testThreadpool(); // 该测试用例先新建了一个能包含4个线程的线程池,首先往里面添加一个任务,该任务是由CallBack封装的普通函数,
return 0; // 先睡1秒,再打印参数3, 线程池分配一个线程来执行这个任务。然后用10次循环来添加任务,该任务是由CallBack封装的
// lamda表达式,依次打印hello 循环下标, world 循环下标。由于线程池最多只能开4个线程,再依次继续新建3个线程执行
} // 执行任务以后,剩下的任务只能先入任务队列并等待之前已经新建的线程来取任务执行。

运行结果:

[daq@centos build]$ ./hello-exe/cmake-good
hello hello 10
world 1
hello 3
world 3
hello 4
world 4
hello 5
world 5
hello 6
world 6
hello 7
world 7
hello 8
world 8
hello hello 9
world 9 world 0
2
world 2
a = 3
All tasks complted.

  

一个线程池的c++实现的更多相关文章

  1. 二 Java利用等待/通知机制实现一个线程池

    接着上一篇博客的 一Java线程的等待/通知模型 ,没有看过的建议先看一下.下面我们用等待通知机制来实现一个线程池 线程的任务就以打印一行文本来模拟耗时的任务.主要代码如下: 1  定义一个任务的接口 ...

  2. ExecutorService实际上是一个线程池的管理工具

    在Java5之后,并发线程这块发生了根本的变化,最重要的莫过于新的启动.调度.管理线程的一大堆API了.在Java5以后,通过Executor来启动线程比用 Thread的start()更好.在新特征 ...

  3. python创建一个线程和一个线程池

    创建一个线程 1.示例代码 import time import threading def task(arg): time.sleep(2) while True: num = input('> ...

  4. Android 性能优化(16)线程优化:Creating a Manager for Multiple Threads 如何创建一个线程池管理类

    Creating a Manager for Multiple Threads 1.You should also read Processes and Threads The previous le ...

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

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

  6. 死磕 java线程系列之自己动手写一个线程池(续)

    (手机横屏看源码更方便) 问题 (1)自己动手写的线程池如何支持带返回值的任务呢? (2)如果任务执行的过程中抛出异常了该怎么处理呢? 简介 上一章我们自己动手写了一个线程池,但是它是不支持带返回值的 ...

  7. 手写一个线程池,带你学习ThreadPoolExecutor线程池实现原理

    摘要:从手写线程池开始,逐步的分析这些代码在Java的线程池中是如何实现的. 本文分享自华为云社区<手写线程池,对照学习ThreadPoolExecutor线程池实现原理!>,作者:小傅哥 ...

  8. [转]使用VC/MFC创建一个线程池

    许多应用程序创建的线程花费了大量时间在睡眠状态来等待事件的发生.还有一些线程进入睡眠状态后定期被唤醒以轮询工作方式来改变或者更新状态信息.线程池可以让你更有效地使用线程,它为你的应用程序提供一个由系统 ...

  9. 在Linux下写一个线程池以及线程池的一些用法和注意点

    -->线程池介绍(大部分来自网络)  在这个部分,详细的介绍一下线程池的作用以及它的技术背景以及他提供的一些服务等.大部分内容来自我日常生活中在网络中学习到的一些概念性的东西. -->代码 ...

  10. java如何自定义一个线程池

    java线程池的一些简单功能,后续会更新,代码不多,很好理解 package com.rbac.thread; import java.util.ArrayList; import java.util ...

随机推荐

  1. win10 U盘重装系统

    1.做好U盘 2.F7选择U盘启动,不用F2切换启动顺序 3.IQY一键安装 4.重启前拔掉U盘 5.如果重启后蓝屏显示 恢复,重新进入PE使用 windows引导恢复,再重新启动

  2. 关于 dangerouslySetInnerHTML

    之前不太了解react,第一次看dangerouslySetInnerHTML,以为可以用来防止xss注入. 后面看待又补充了一个DOMPurify,才知道这个东西只有一个提醒的作用,并不能防止xss ...

  3. CC2530

    代码操作外设的一般步骤:• 1. 将代码编译成CPU能识别的语言• 2. cpu解析执行代码流• 3. 然后通过总线找到外设连接的控制器的寄存器(即SFR),通过设置这些寄存器,来指挥控制器工作.

  4. CAD安装失败,如何才能删除(卸载)CAD?三步完美卸载CAD软件

    CAD安装失败,怎么样才能彻底删除CAD各种残留注册表?有时候我们想将CAD重新安装,但却显示CAD已经安装,但又不见CAD软件程序文件,然后我们就会产生这样的疑问:CAD无法重新安装是怎么一回事?下 ...

  5. qt 单元测试遇到的问题

    升级了qt creator到新版本.使用 google 单元测试,发现一个单元测试显示不对了. 测试结果输出:FATAL, 项目"t_ps"的测试未产生任何预期输出 比较了几个测试 ...

  6. 《Vue.js 3.x高效前端开发(视频教学版)》源码课件同步教学视频免费下载

    <Vue.js 3.x高效前端开发(视频教学版)>源码课件同步教学视频免费下载.获得出版社和作者授权,可用于个人学习使用,禁止任何形式的商用.

  7. 转载安卓或苹果手机获取URL scheme方法

    首先,打开支付宝,来到需要抓取的小程序页面.此处以上海的随申码为例演示,打开随申办小程序,点击顶部的「随申码」按钮进入页面. 可以看出这个一个二级页面,如果想要使用 URL Scheme 一键访问,同 ...

  8. AES可以加密成

    AES可以生成HEX的字符串如{[m9LJfF4fYtt+PGoAA5WmL+6RFh5oVvrlCQpVhLyk3l28XqS3D4Qd+ehOdvqLcUrFKcyBV3hygXHU3We33bY ...

  9. 样本方差S²中为什么是乘以1/(n-1)或者说除以n-1?贝塞尔校正,无偏估计

    样本方差S²中为什么是乘以1/(n-1)或者说除以n-1?贝塞尔校正,无偏估计 前言:重在记录,可能出错. 先看样本方差的公式如下: S2=1n-1∑i=1n(Xi-X¯)2=1n-1(∑i=1nXi ...

  10. 2357. 使数组中所有元素都等于零 (Easy)

    问题描述 2357. 使数组中所有元素都等于零 (Easy) 给你一个非负整数数组 nums .在一步操作中,你必须: 选出一个正整数 x , x 需要小于或等于 nums 中 最小 的 非零 元素. ...