手把手教你撸一个线程池 - 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,一经查实,立即删除!

相关文章

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

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

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

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

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

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

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

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

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

计算机专业想来是广大美国研究生申请的热门专业&#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…

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;要有良好的…

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…

C语言贪吃蛇小游戏 | 源码

点击蓝字关注我们该程序已经在VS2019上验证过了&#xff0c;可以直接运行。程序目录程序里就只有一个test.cpp文件&#xff0c;也就是说大家可以先创建工程&#xff0c;然后直运行效果C语言学习资源汇总【最新版】源码#include<stdio.h> #include<stdlib.h> #inclu…

东华大学计算机学院讲座单,计信学院成功举办2018级专业导师面对面系列讲座活动...

为帮助大一新生尽快适应大学生活&#xff0c;了解专业&#xff0c;掌握知识&#xff0c;计信学院积极响应学校号召&#xff0c;成立由教授、博士组成的专业导师团队&#xff0c;为各专业开展系列专业知识讲座。自10月17日晚7点至10月26日下午4点&#xff0c;各专业导师面对面系…

C函数指针别再停留在语法,得上升到软件设计

点击蓝字关注我们经常有朋友问到底如何在C程序中采用面向对象编程?如何让模块之间松耦合&#xff1f;......其实究其原因还是没有把C语言与软件设计很好的联系起来。这里跟大家找了一些内容&#xff0c;相信认真看完全文多多少少会有你想要的答案&#xff1a;正文部分&#xf…

spring默认缓存管理器_使用Spring的缓存管理器缓存Web内容

spring默认缓存管理器在这篇文章中&#xff0c;我想向大家展示如何使用Spring的CacheManager&#xff0c; Cacheable和JMX批注来缓存和管理Web内容的缓存的基础知识。 想象一下一个网上商店&#xff0c;它从远程WCMS&#xff08;Web内容管理系统&#xff09;获取一些内容&#…

计算机软件选修课选什么好,大学值得选的“选修课”,一点不比专业课差,能选上最好认真听...

原标题&#xff1a;大学值得选的“选修课”&#xff0c;一点不比专业课差&#xff0c;能选上最好认真听文/小哈你是怎样对待你的选修课的&#xff1f;以前经常听学长学姐们说过这样一句话&#xff0c;“上大学之后&#xff0c;专业课选逃&#xff0c;选修课必逃”。不知道大家是…

C/C++程序员的编程修养

点击蓝字关注我们什么是好的C/C程序员&#xff1f;是不是懂得很多技术细节&#xff1f;还是懂底层编程&#xff1f;还是编程速度比较快&#xff1f;我觉得都不是。对于一些技术细节来说和底层的技术&#xff0c;只要看帮助&#xff0c;查资料就能找到&#xff0c;对于速度快&am…