C++多线程快速入门(五)简单线程池设计

目录

  • 设计思路
  • 主线程运行逻辑
  • task以及taskpool设计
  • 详细流程讲解
  • 完整代码
  • 打印结果
  • 往期回顾

设计思路

线程池实际上就是一组线程,当我们需要异步执行一些任务时,经常要通过OS频繁创建和销毁线程,不如直接创建一组在程序生命周期内不会退出的线程。
当有任务需要被执行时,线程可以自动地去拿任务并执行,在没有任务时,线程处于阻塞或者睡眠状态。
我们选择队列存放任务,main函数中的thread作为任务的生产者,从队列尾部插入任务。线程池中的线程作为消费者,从队列头部获取任务。
复杂考虑的话,当线程池的线程在处理任务的过程中也产生了关联任务,那么这个线程也是消费者。

队列也可以设计得更加具有实用性,例如可以根据任务的优先级设计多个队列,然后线程根据优先级获取线程(也就是先查询高优先级的队列,为空再去查询低优先级的队列)。

综上,我们的设计中会有多个线程访问任务队列,所以我们要解决线程池创建向队列投放任务从队列中获取任务的线程互斥性。
同时线程池的清理退出线程池中的工作线程清理任务队列,也是需要考虑的。
并且,为了能够详细获知多线程获取多任务的流程,我们需要对taskID和threadID进行输出打印,std::cout并不是线程安全的,所以我们也要实现互斥地cout。
这里我们统一使用互斥量std::mutex+lock来实现互斥性

主线程运行逻辑

在这里插入图片描述

task以及taskpool设计

Task
在这里插入图片描述
TaskPool
在这里插入图片描述
内部变量
在这里插入图片描述

详细流程讲解

1、创建线程池对象时,调用构造函数TaskPool(),并初始化布尔类型的标志m_bRunning为false,表示此时线程池对象中的线程不工作
2、调用线程池对象的初始化函数init(),这里默认的线程数为5。

  • m_bRunning置为true,表示线程池对象中的线程应该开始运行了
  • 然后通过for循环,每次构造一个新的线程对象,并且绑定一个线程函数TaskPool::threadFunc,创建之后线程就开始工作了。然后打印出当前的线程id,注意此时需要上锁,保证cout输出正常。然后将线程对象送入m_threads数组
    3、接下来看看构造出来的每个线程在干啥:
while(true)
{{上锁,访问任务队列// 注意由于队列本身是个多线程共享资源,所以对于队列取元素以及状态判断都是要先加锁再操作队列为空则一直循环  // 为什么要用循环判断呢?// 这是因为wait()从阻塞到返回,不一定就是由于notify_one()函数造成的,还有可能由于系统的不确定原因唤醒(可能和条件变量的实现机制有关),这个的时机和频率都是不确定的,被称作伪唤醒,如果在错误的时候被唤醒了,执行后面的语句就会错误,所以需要再次判断队列是否为空,如果还是为空,就继续wait()阻塞。{如果m_bRunning为false,说明此时应该中止线程操作,所以需要连着跳出两个循环,所以直接用goto label吧否则就一直等待在这儿,wait()可以让线程陷入休眠状态,在消费者生产者模型中,如果生产者发现队列中没有东西,就可以让自己休眠.}此时 获取队头元素,并将队头元素出队}// 为了减少锁的粒度,接下来的操作不需要加锁了,因为已经拿到了队列中的元素执行Task对象的doIt()方法,也就是打印任务id和线程id
}
label :
打印当前线程id

4、for循环,创建10个Task对象,然后调用addTask方法,将task送入线程池中的任务队列。显然push操作是互斥的,所以需要先上锁。然后打印任务id和线程id,最后通过条件变量的notify_one方法,通知一个挂起的线程去消费队列里面的任务。

5、等待一段时间
6、调用线程池对象的stop方法,先设置m_bRunning标志为false,然后通过条件变量的notify_all方法,通知挂起的或者正在运行的所有线程,结束线程函数运行。然后等待所有线程join之后,退出。
7、跳出主线程,开始调用TaskPool对象的析构函数,也就是执行removeAllTasks方法,也就是将任务队列里面的存的task指针进行reset,也就是减引用计数,shared_ptr指针如果引用计数减为0,会自动调用析构函数。为了线程安全,我们同样需要对这块代码进行加锁。

完整代码

c++实现简单线程池代码

打印结果

Init a thread, id: 2
Init a thread, id: 3
Init a thread, id: 4
Init a thread, id: 5
Init a thread, id: 6
add a Task, id: 0, thread id is: 1
add a Task, id: 1, thread id is: 1
add a Task, id: 2, thread id is: 1
handle a task ,TaskID is: 1, thradID is:3
a task destructed , TaskID is: 1, thradID is:3
handle a task ,TaskID is: 2, thradID is:4
a task destructed , TaskID is: 2, thradID is:4
handle a task ,TaskID is: 0, thradID is:2
a task destructed , TaskID is: 0, thradID is:2
add a Task, id: 3, thread id is: 1
handle a task ,TaskID is: 3, thradID is:3
a task destructed , TaskID is: 3, thradID is:3
handle a task ,TaskID is: 4, thradID is:5
add a Task, id: 4, thread id is: 1
a task destructed , TaskID is: 4, thradID is:1
add a Task, id: 5, thread id is: 1
handle a task ,TaskID is: 5, thradID is:4
a task destructed , TaskID is: 5, thradID is:4
add a Task, id: 6, thread id is: 1
handle a task ,TaskID is: 6, thradID is:4
a task destructed , TaskID is: 6, thradID is:4
handle a task ,TaskID is: 7, thradID is:2
add a Task, id: 7, thread id is: 1
a task destructed , TaskID is: 7, thradID is:1
add a Task, id: 8, thread id is: 1
add a Task, id: 9, thread id is: 1
handle a task ,TaskID is: 9, thradID is:6
a task destructed , TaskID is: 9, thradID is:6
handle a task ,TaskID is: 8, thradID is:5
a task destructed , TaskID is: 8, thradID is:5
exit thread , threadID:3
exit thread , threadID:4
exit thread , threadID:2
exit thread , threadID:5
exit thread , threadID:6Process finished with exit code 0

往期回顾

C++多线程快速入门(四)shared_mutex以及读写锁应用
C++多线程快速入门(三):生产者消费者模型与条件变量使用
C++多线程快速入门(二)共享数据同步以及数据竞争
C++多线程快速入门(一):基本&常用操作

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

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

相关文章

C++网络编程快速入门(一):TCP网络通信基本流程以及基础函数使用

目录流程概述服务器端代码实现客户端代码实现函数和结构讲解sockaddr_in和sockaddrsocket : 创建一个socket连接bind :绑定地址以及端口号问题流程概述 客户端与服务器之间的网络通信基本原理如下所示,复杂一点的架构可能会添加消息中间件。…

使用前端框架Foundation 4来帮助简化响应式设计开发

日期:2013-3-12 来源:GBin1.com Foundation是一套使用广泛的前端开发套件,可以帮助你快速的网站。最近ZURB发布了一个新版本的Foundation 4前端框架,能够有效的帮助你快速的开发响应式的网站。 和另外一个套知名的前端框架BootSt…

C++网络编程快速入门(二):Linux下使用select演示简单服务端程序

目录select参数解释select使用规范select使用缺点基本流程实例代码通信效果演示往期文章select参数解释 extern int select (int __nfds, fd_set *__restrict __readfds,fd_set *__restrict __writefds,fd_set *__restrict __exceptfds,struct timeval *__restrict __timeout)…

Android转载一:Android文件命名规范

REF:http://blog.csdn.net/gulianchao/article/details/23391651 (一) Layout命名 1.contentview命名:activity_功能模块.xml 例如:activity_main.xml、activity_more.xml 2.Dialog命名:dialog_描述.xml …

C++网络编程快速入门(三):阻塞与非阻塞式调用网络通信函数

目录阻塞与非阻塞定义send与recvconnect一些问题为什么要将监听socket设置为非阻塞阻塞与非阻塞定义 阻塞模式指的是当前某个函数执行效果未达预期,该函数会阻塞当前的执行线程,程序执行流在超时时间到达或者执行成功后恢复原有流程。非阻塞模式相反&am…

socket 端口和地址复用

https://blog.csdn.net/weibo1230123/article/details/79978745 https://blog.csdn.net/weixin_42157432/article/details/115560824 在linux socket网络编程中,大规模并发TCP或UDP连接时,经常会用到端口复用: int opt 1; if (setsockopt…

MyEclipse老是弹出problem occurred窗口

有的时候是因为jsp页面中的java脚本有误&#xff0c;比如说<% String name"";>就会出现错误&#xff0c;因为结束标签少了一个百分号&#xff05;。转载于:https://www.cnblogs.com/passer1991/archive/2013/03/15/2961624.html

Mysql中代替like模糊查询的一种方法

使用Mysql的函数instr,可代替传统的like方式查询,并且速度更快。 instr函数&#xff0c;第一个参数是字段&#xff0c;第二个参数是要查询的串&#xff0c;返回串的位置&#xff0c;第一个是1&#xff0c;如果没找到就是0. 例如&#xff1a; select username from prefix_user …

Linux网络故障排查命令(ifconfig、ping、telnet、netstat、lsof、nc、curl、tcpdump)

目录ifconfig-s&#xff0c;显示网卡信息的精简列表-a、up、down将IP地址绑定到某个网卡&#xff0c;以及解绑操作pingtelnetnetstatlsofnc模拟一个服务器程序和客户端程序进行通信发送文件curltcpdump参数连接一个正常的监听端口ifconfig 该命令用来查看当前系统的网卡和IP地…

My Oracle Support Metalink站点最近将放弃flash界面转而使用ADF HTML

根据oracle官方博客的报道《The New My Oracle Support User Interface (HTML-based) 》&#xff0c; MY ORACLE SUPPORT开发team会在最近将support.oracle.com站点从原来的flash界面迁移到基于ADF HTML的用户界面上。 实际上在2012年的 January 27&#xff0c; MOS开发team就…

心跳检测以及应用层心跳包机制设计

博主联系方式&#xff1a; QQ:1540984562 微信&#xff1a;wxid_nz49532kbh9u22 QQ交流群&#xff1a;750313950&#xff08;嵌入式方向&#xff09; QQ交流群&#xff1a;856398158&#xff08;后端方向&#xff09; 目录心跳检测应用场景死连接情况保活传递有效业务数据心跳包…

一个DBA的工作写照

一个DBA的工作写照&#xff0c; 一个DBA的内心 Know the DBA Mind! DBA也是 IT民工啊&#xff0c; 民工何苦为难民工&#xff01; 转载于:https://www.cnblogs.com/macleanoracle/archive/2013/03/19/2968227.html

UVALive 6257 Chemist's vows --一道题的三种解法(模拟,DFS,DP)

题意&#xff1a;给一个元素周期表的元素符号&#xff08;114种&#xff09;&#xff0c;再给一个串&#xff0c;问这个串能否有这些元素符号组成&#xff08;全为小写&#xff09;。 解法1&#xff1a;动态规划 定义&#xff1a;dp[i]表示到 i 这个字符为止&#xff0c;能否有…

hdu 1025(最长非递减子序列的n*log(n)求法)

题目链接&#xff1a;http://acm.hdu.edu.cn/showproblem.php?pid1025 经典题。。。最长非递减序列的n*log(n)求法。。。orz... View Code 1 #include<iostream>2 const int N500007;3 using namespace std;4 int city[N];5 int dp[N];//dp[i]保存的是长度为i的最长不降…

消息队列重要机制讲解以及MQ设计思路(kafka、rabbitmq、rocketmq)

目录《Kafka篇》简述kafka的架构设计原理&#xff08;入口点&#xff09;消息队列有哪些作用&#xff08;简单&#xff09;消息队列的优缺点&#xff0c;使用场景&#xff08;基础&#xff09;消息队列如何保证消息可靠传输死信队列是什么&#xff1f;延时队列是什么&#xff1…

数据库归档模式

1、在sys身份下登陆oracle&#xff0c;执行命令archive log list; SQL> archive log list; Database log mode Archive Mode Automatic archival Enabled Archive destination USE_DB_RECOVERY_FILE_DEST Oldest online log sequence …

转载|网络编程中阻塞式函数的底层逻辑

逛知乎看到的&#xff0c;觉得写的挺透彻的&#xff0c;转载一下&#xff0c;原文链接&#xff1a;Unix网络编程里的阻塞是在操作系统的内核态创建一个线程来死循环吗&#xff1f; 原文以阻塞式的recv函数作为讲解&#xff0c;但是所有阻塞式的api底层逻辑基本相通。 下面是正文…

树的存储结构2 - 数据结构和算法42

树的存储结构 让编程改变世界 Change the world by program 孩子表示法 我们这次换个角度来考虑&#xff0c;由于树中每个结点可能有多棵子树&#xff0c;可以考虑用多重链表来实现。 就像我们虽然有计划生育&#xff0c;但我们还是无法确保每个家庭只养育一个孩子的冲动&a…

Sharepoint 2013 发布功能(Publishing features)

一、默认情况下&#xff0c;在创建网站集时&#xff0c;只有选择的模板为‘ Publishing Portal&#xff08;发布门户&#xff09;’与‘ Enterprise Wiki&#xff08;企业 Wiki&#xff09;’时才默认启用发布功能&#xff0c;如下图所示&#xff1a; 二、发布功能包含两块&…

【草稿】windows + vscode 远程开发

主要分为三个步骤&#xff1a; 1、开启openssh服务 2、通过ssh命令连接到远程服务器 3、通过vscode连接远程服务器进行开发调试 ssh概念 SSH是较可靠&#xff0c;专为远程登陆会话和其他网络服务提供安全性得协议&#xff0c;利用ssh协议可以有效防止远程管理过程中得信息…