手把手教你撸一个线程池 - C++版

点击蓝字

2c272d387ce85b7d95fb1904d8ec82d9.png

关注我们

前面的文章在手把手教你写 C 语言线程池中,已经实现了 C 语言版的线程池,如果我们也学过 C++ 的话,可以将其改为 C++ 版本,这样代码不管是从使用还是从感观上都会更简洁一些。

对这些代码做从 C 到 C++ 的迁移主要用到了 C++ 三大特性中的封装,因此难度不大,对应 C++ 初学者来说有助于提高编码水平和对面向对象的理解,对于熟练掌握了 C++ 的人来说就是张飞吃豆芽 -- 小菜一碟(so easy)。

关于线程的在此就不再过多阐述,对于前面文章中设计的线程池,按照面向对象的思想进行拆分可以分为两部分(纯属个人见解,有不同的想法也正常):任务队列类 和线程池类。

一、任务队列

1、类声明

// 定义任务结构体
using callback = void(*)(void*);
struct Task
{Task(){function = nullptr;arg = nullptr;}Task(callback f, void* arg){function = f;this->arg = arg;}callback function;void* arg;
};// 任务队列
class TaskQueue
{
public:TaskQueue();~TaskQueue();// 添加任务void addTask(Task& task);void addTask(callback func, void* arg);// 取出一个任务Task takeTask();// 获取当前队列中任务个数inline int taskNumber()
{return m_queue.size();}private:pthread_mutex_t m_mutex;    // 互斥锁std::queue<Task> m_queue;   // 任务队列
};

其中 Task 是任务类,里边有两个成员,分别是两个指针 void(*)(void*) 和 void*

另外一个类 TaskQueue 是任务队列,提供了添加任务、取出任务、存储任务、获取任务个数、线程同步的功能。

2、类定义

TaskQueue::TaskQueue()
{pthread_mutex_init(&m_mutex, NULL);
}TaskQueue::~TaskQueue()
{pthread_mutex_destroy(&m_mutex);
}void TaskQueue::addTask(Task& task)
{pthread_mutex_lock(&m_mutex);m_queue.push(task);pthread_mutex_unlock(&m_mutex);
}void TaskQueue::addTask(callback func, void* arg)
{pthread_mutex_lock(&m_mutex);Task task;task.function = func;task.arg = arg;m_queue.push(task);pthread_mutex_unlock(&m_mutex);
}Task TaskQueue::takeTask()
{Task t;pthread_mutex_lock(&m_mutex);if (m_queue.size() > 0){t = m_queue.front();m_queue.pop();}pthread_mutex_unlock(&m_mutex);return t;
}

二、线程池

1、类声明

class ThreadPool
{
public:ThreadPool(int min, int max);~ThreadPool();// 添加任务void addTask(Task task);// 获取忙线程的个数int getBusyNumber();// 获取活着的线程个数int getAliveNumber();private:// 工作的线程的任务函数static void* worker(void* arg);// 管理者线程的任务函数static void* manager(void* arg);void threadExit();private:pthread_mutex_t m_lock;pthread_cond_t m_notEmpty;pthread_t* m_threadIDs;pthread_t m_managerID;TaskQueue* m_taskQ;int m_minNum;int m_maxNum;int m_busyNum;int m_aliveNum;int m_exitNum;bool m_shutdown = false;
};

2、类定义

ThreadPool::ThreadPool(int minNum, int maxNum)
{// 实例化任务队列m_taskQ = new TaskQueue;do {// 初始化线程池m_minNum = minNum;m_maxNum = maxNum;m_busyNum = 0;m_aliveNum = minNum;// 根据线程的最大上限给线程数组分配内存m_threadIDs = new pthread_t[maxNum];if (m_threadIDs == nullptr){cout << "malloc thread_t[] 失败...." << endl;;break;}// 初始化memset(m_threadIDs, 0, sizeof(pthread_t) * maxNum);// 初始化互斥锁,条件变量if (pthread_mutex_init(&m_lock, NULL) != 0 ||pthread_cond_init(&m_notEmpty, NULL) != 0){cout << "init mutex or condition fail..." << endl;break;}/// 创建线程 //// 根据最小线程个数, 创建线程for (int i = 0; i < minNum; ++i){pthread_create(&m_threadIDs[i], NULL, worker, this);cout << "创建子线程, ID: " << to_string(m_threadIDs[i]) << endl;}// 创建管理者线程, 1个pthread_create(&m_managerID, NULL, manager, this);} while (0);
}ThreadPool::~ThreadPool()
{m_shutdown = 1;// 销毁管理者线程pthread_join(m_managerID, NULL);// 唤醒所有消费者线程for (int i = 0; i < m_aliveNum; ++i){pthread_cond_signal(&m_notEmpty);}if (m_taskQ) delete m_taskQ;if (m_threadIDs) delete[]m_threadIDs;pthread_mutex_destroy(&m_lock);pthread_cond_destroy(&m_notEmpty);
}void ThreadPool::addTask(Task task)
{if (m_shutdown){return;}// 添加任务,不需要加锁,任务队列中有锁m_taskQ->addTask(task);// 唤醒工作的线程pthread_cond_signal(&m_notEmpty);
}int ThreadPool::getAliveNumber()
{int threadNum = 0;pthread_mutex_lock(&m_lock);threadNum = m_aliveNum;pthread_mutex_unlock(&m_lock);return threadNum;
}int ThreadPool::getBusyNumber()
{int busyNum = 0;pthread_mutex_lock(&m_lock);busyNum = m_busyNum;pthread_mutex_unlock(&m_lock);return busyNum;
}// 工作线程任务函数
void* ThreadPool::worker(void* arg)
{ThreadPool* pool = static_cast<ThreadPool*>(arg);// 一直不停的工作while (true){// 访问任务队列(共享资源)加锁pthread_mutex_lock(&pool->m_lock);// 判断任务队列是否为空, 如果为空工作线程阻塞while (pool->m_taskQ->taskNumber() == 0 && !pool->m_shutdown){cout << "thread " << to_string(pthread_self()) << " waiting..." << endl;// 阻塞线程pthread_cond_wait(&pool->m_notEmpty, &pool->m_lock);// 解除阻塞之后, 判断是否要销毁线程if (pool->m_exitNum > 0){pool->m_exitNum--;if (pool->m_aliveNum > pool->m_minNum){pool->m_aliveNum--;pthread_mutex_unlock(&pool->m_lock);pool->threadExit();}}}// 判断线程池是否被关闭了if (pool->m_shutdown){pthread_mutex_unlock(&pool->m_lock);pool->threadExit();}// 从任务队列中取出一个任务Task task = pool->m_taskQ->takeTask();// 工作的线程+1pool->m_busyNum++;// 线程池解锁pthread_mutex_unlock(&pool->m_lock);// 执行任务cout << "thread " << to_string(pthread_self()) << " start working..." << endl;task.function(task.arg);delete task.arg;task.arg = nullptr;// 任务处理结束cout << "thread " << to_string(pthread_self()) << " end working...";pthread_mutex_lock(&pool->m_lock);pool->m_busyNum--;pthread_mutex_unlock(&pool->m_lock);}return nullptr;
}// 管理者线程任务函数
void* ThreadPool::manager(void* arg)
{ThreadPool* pool = static_cast<ThreadPool*>(arg);// 如果线程池没有关闭, 就一直检测while (!pool->m_shutdown){// 每隔5s检测一次sleep(5);// 取出线程池中的任务数和线程数量//  取出工作的线程池数量pthread_mutex_lock(&pool->m_lock);int queueSize = pool->m_taskQ->taskNumber();int liveNum = pool->m_aliveNum;int busyNum = pool->m_busyNum;pthread_mutex_unlock(&pool->m_lock);// 创建线程const int NUMBER = 2;// 当前任务个数>存活的线程数 && 存活的线程数<最大线程个数if (queueSize > liveNum && liveNum < pool->m_maxNum){// 线程池加锁pthread_mutex_lock(&pool->m_lock);int num = 0;for (int i = 0; i < pool->m_maxNum && num < NUMBER&& pool->m_aliveNum < pool->m_maxNum; ++i){if (pool->m_threadIDs[i] == 0){pthread_create(&pool->m_threadIDs[i], NULL, worker, pool);num++;pool->m_aliveNum++;}}pthread_mutex_unlock(&pool->m_lock);}// 销毁多余的线程// 忙线程*2 < 存活的线程数目 && 存活的线程数 > 最小线程数量if (busyNum * 2 < liveNum && liveNum > pool->m_minNum){pthread_mutex_lock(&pool->m_lock);pool->m_exitNum = NUMBER;pthread_mutex_unlock(&pool->m_lock);for (int i = 0; i < NUMBER; ++i){pthread_cond_signal(&pool->m_notEmpty);}}}return nullptr;
}// 线程退出
void ThreadPool::threadExit()
{pthread_t tid = pthread_self();for (int i = 0; i < m_maxNum; ++i){if (m_threadIDs[i] == tid){cout << "threadExit() function: thread " << to_string(pthread_self()) << " exiting..." << endl;m_threadIDs[i] = 0;break;}}pthread_exit(NULL);
}

*声明:本文于网络整理,版权归原作者所有,如来源信息有误或侵犯权益,请联系我们删除或授权事宜。

4c65c4505f617a6680744d92713651df.png

ecc20c83360a3540844077d86819a7fa.gif

戳“阅读原文”我们一起进步

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/337645.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

考研计算机专业课复习,考研中计算机专业课的复习考试.pdf

考研中计算机专业课的复习考试.pdf第20卷第1期 西昌学院学报自然科学版 VoL20-№1of ScienceEdition Mar&#xff0e;&#xff0e;20062006年3月 Joumal College‘NaturalXichang考研中计算机专业课的复习考试伍治林&#xff0c;伍迪&#xff0c;唐韬&#xff0c;褚晓锐(西昌学…

备忘录怎么用红笔标注_错题本的正确打开方法,你用对了吗?

相信很多考生都会有自己的错题本&#xff0c;可是并不是每个考生的错题本都起到了作用。那么错题本究竟如何做才可以切实可用又可以做的好呢&#xff1f;大禹未来教育收集整理了相关内容&#xff0c;分享给大家&#xff01;1、制作错题本的步骤步骤一&#xff1a;把所有的练习册…

spring解密_在运行时更新代码(已Spring解密)

spring解密当从编译到部署再到测试的开发周期花费太长时间时&#xff0c;人们希望能够及时替换正在运行的代码&#xff0c;而无需重新启动应用程序服务器并等待部署完成。 在这种情况下&#xff0c;像JRebel这样的商业解决方案或像Grails这样的开源框架就可以提供帮助。 JVM不…

组装台式计算机需要哪些配件,组装电脑需要哪些配件 电脑装机教程图解

DIY电脑一直都是电脑爱好者非常喜欢的&#xff0c;对于电脑爱好者来说&#xff0c;通过自己动手DIY组装电脑&#xff0c;不仅可以更为深刻的认识硬件&#xff0c;了解电脑主机内部结构&#xff0c;还能够学习到安装系统等知识&#xff0c;从中可以带来很多乐趣。今天我们为大家…

C语言代码优化的实用方法大总结

点击蓝字关注我们1、选择合适的算法和数据结构选择一种合适的数据结构很重要&#xff0c;如果在一堆随机存放的数中使用了大量的插入和删除指令&#xff0c;那使用链表要快得多。数组与指针语句具有十分密切的关系&#xff0c;一般来说&#xff0c;指针比较灵活简洁&#xff0c…

怎么取消苹果手机自动续费_手机腾讯视频的vip怎么取消自动续费?

如果是安卓手机直接在腾讯视频的【个人中心】找到【我的VIP会员】即可选择【管理自动续费】取消自动续费。如果是苹果手机需要在Apple ID中找到【订阅】选项&#xff0c;取消订阅即可。以下是详细介绍&#xff1a;1、如果您是安卓手机直接点击进入【腾讯视频】的应用&#xff0…

java转换为c#_将25k行C#转换为Java的经验教训

java转换为c#由于各种原因&#xff0c;我最近完成了一个将复杂的财务应用程序从C&#xff03;转换为Java的项目。 港口的原因大部分是非技术性的&#xff0c;而是对相关企业的一项战略举措。 这是一次有趣的经历&#xff0c;我在此过程中吸取了一些经验教训&#xff0c;可以分…

计算机专业去荷兰还是瑞典,给申请计算机专业的同学几点建议

计算机专业想来是广大美国研究生申请的热门专业&#xff0c; 这里所说的热门&#xff0c;不光指招生人数多&#xff0c; 就业前景优势也是无容置疑的。广大申请者的硬件条件无外乎分为以下几种情况&#xff1a;1、托福80&#xff0c;GRE 3002、托福90 GRE3153、托福100&#xf…

为何某些公司不允许使用C++STL

点击蓝字关注我们最初开始禁用 C STL&#xff0c;是因为早期项目编码实践中留下的惯例&#xff0c;被后来的程序员继承下来。老项目中这种选择尤其地多。不过如果有人将其上升到公司行为在不同项目中全面禁用 STL&#xff0c;则没有必要&#xff0c;而且我倾向于做这种决定的人…

支持向量机python代码_Python中的支持向量机SVM的使用(有实例)

除了在Matlab中使用PRTools工具箱中的svm算法&#xff0c;Python中一样可以使用支持向量机做分类。因为Python中的sklearn库也集成了SVM算法&#xff0c;本文的运行环境是Pycharm。 一、导入sklearn算法包 skleran中集成了许多算法&#xff0c;其导入包的方式如下所示&#xff…

swarm:pending_WildFly Swarm:使用Java EE构建微服务

swarm:pending“完美无缺&#xff0c;不是在没有其他可添加的东西时&#xff0c;而是在没有其他东西要带走时实现的” Antoine de Saint-Exupery 法国作家安托万德圣艾修伯里 &#xff08; Antoine de Saint-Exupery &#xff09;的这句话是为了证实通常少花钱多。 对于建筑师…

html中引入js柱状图,Echarts.js 图表在layui中的引用

添加部分源码,可以使layui使用第三方插件的方法引入,代码以下://打开源码,将代码头部的代码(function (global, factory) {typeof exports object && typeof module ! undefined ? factory(exports) :typeof define function && define.amd ? define([expo…

C++ 23将引入标准库模块支持,编译速度提升10倍

点击蓝字关注我们虽然模块是 C 20 的主要卖点&#xff0c;但与 C 20 不同的是&#xff0c;C 23 的标准库也将实现模块化。从标准库开始&#xff0c;模块有望改善编译速度和 C 开发者组织代码的方式。C 是丹麦计算机科学家 Bjarne Stroustrup 创建的编程语言&#xff0c;由 ISO …

python刷题_11.学习Python,刷题才能让你感受到快(差)乐(距)!

猜数字游戏可用一个简单的while循环来实现。 其中 随机数 的生成在第4篇介绍过&#xff0c; 键盘输入在上一篇介绍过 import random result round(random.random()*100) while result ! guess: guess int(input()) if guess>result: print(大了!) elif guess print(小了!)…

java jigsaw_Java 9和Project Jigsaw如何破坏您的代码

java jigsawJava 9迫在眉睫&#xff0c;它将带有完整的Project Jigsaw 。 在我从OpenJFX邮件列表的最近讨论中得知它可能会破坏现有代码之前&#xff0c;我并没有对此给予太多关注。 这对于Java非常不寻常&#xff0c;因此引起了我的兴趣。 我阅读了该项目的JEP和一些相关文章…

C语言的万能“三板斧”

点击蓝字关注我们作为嵌入式工程师&#xff0c;写一个效率高效&#xff0c;思路清晰的C语言程序是我们的终极目标。那么&#xff0c;怎样才能写好这样的程序呢&#xff1f;首先&#xff0c;我们要用C语言的思维方式来进行程序的构架构建&#xff1b;其次&#xff0c;要有良好的…

武汉城市职业学院计算机分数线,武汉城市职业学院历年分数线 2021武汉城市职业学院录取分数线...

一、2019年武汉城市职业学院各省录取分数线及录取位次统计表1、武汉城市职业学院2019年在福建录取分数线如下&#xff1a;文科录取批次高职专科301分&#xff0c;最低录取位次为68896名、理科录取批次高职专科229分,最低录取位次为130149名&#xff1b;2、武汉城市职业学院2019…

aws sqs_在Spring中将AWS SQS用作JMS提供程序

aws sqs最近AWS公布了新的客户端库&#xff0c;它实现了JMS 1.1规范 &#xff0c;并使用他们的简单队列服务 &#xff08;SQS&#xff09;作为JMS提供者 &#xff08;见杰夫巴尔的帖子在这里 &#xff09;。 在我的文章中&#xff0c;我将向您展示如何设置Maven项目以使用Sprin…

openmv串口数据 串口助手_STM32 串口接收不定长数据 STM32 USART空闲检测中断

编者注&#xff1a;单片机串口接收不定长数据时&#xff0c;必须面对的一个问题为&#xff1a;怎么判断这一包数据接收完成了呢&#xff1f;常见的方法主要有以下两种&#xff1a;1.在接收数据时启动一个定时器&#xff0c;在指定时间间隔内没有接收到新数据&#xff0c;认为数…

java 多线程性能_Java中多线程的性能比较

java 多线程性能Java中有多种用于多线程的技术。 可以通过同步关键字&#xff0c;锁或原子变量来并行化Java中的一段代码。 这篇文章将比较使用synced关键字ReentrantLock&#xff0c;getAndIncrement&#xff08;&#xff09;以及执行get&#xff08;&#xff09;和compareAnd…