【线程池项目(四)】项目的死锁问题分析和资源回收机制的改善

在上一篇 【线程池项目(三)】线程池CACHED模式的实现中我们大概说了说cached模式的基本实现,对于多线程编程,我们需要考虑的问题也较于单线程更多、更复杂,经常存在线程同步、资源竞争等复杂的并发控制问题,因此上篇的文末留下了三个问题🤔🤔🤔
在这里插入图片描述
这是本篇博客关注的第一个死锁问题,另一个就是在跨平台编译(Ubuntu20.04)时,由于平台差异化而引起的出现的死锁性问题,下面我们来详细的剖析一下

如果需要与本篇博客完全匹配的实现代码,也可以在 我的gitee 上下载对应的源码

项目经历——基于C++新特性以及模板编程实现的线程池

    • 一、三种状态的可能性死锁问题🧐🧐🧐
    • 二、对应的解决方案😮😮😮
    • 三、资源回收机制的改良😦😦😦
    • 四、平台差异化的死锁性问题(没有发生,代码竟然能跑🤣🤣)
    • 五、对应的解决方案🥹🥹🥹
    • 六、总结😩😩😩

一、三种状态的可能性死锁问题🧐🧐🧐

我们先来看看前两种情况

  1. 🤔 当线程数多余任务队列中的任务数或者是任务队列为空时,在一定的时间内,肯定会有某个或者某些线程处于空闲的wait状态等待任务的到来,如果这时用户准备结束主程序(即执行线程池的析构函数),将isPoolRunning置为false,并进行notEmpty.notify_all()通知,等待在wait上的线程被唤醒往下执行,在下面的if判断语句中满足结束条件,正常的回收线程;cached模式通过自己的回收机制也能正常的回收线程,不会造成死锁问题的发生

  2. 🤔 当某个线程正在执行任务exec且还未执行完时,用户准备结束主程序(即执行线程池的析构函数),析构函数中的语句对正在执行任务的线程没有任何影响,当线程执行任务完成且更新完任务执行完的时间后,同样也能够通过while()的判断条件正常的退出,进而被回收,也不会发生死锁问题

这是第三种情况

  1. 🤔 当某个线程执行任务完成,且用户准备结束主程序(即调用线程池的析构函数)时,线程在isPoolRunning被主程序置为false之前,执行完任务的线程已经进入了外边的while循环,并且还没有执行到fixed模式下的wait,这时主线程往下执行析构函数,通知等待的线程,并等待所有线程被回收,之后执行完任务的线程再往下执行至notEmpyt.wait(),显然现在线程池中只有一个线程等待被唤醒,而用户所在的主线程也阻塞在exitCond_wait()上,出现了死锁现象

在这里插入图片描述

🫠以下是正常结束的执行结果(具有偶然性)

在这里插入图片描述

🫠这是发生死锁问题的执行结果,有一个任务线程没有被回收,主程序线程也被无限期等待

在这里插入图片描述

二、对应的解决方案😮😮😮

来看看修改后的代码
在这里插入图片描述

  • 当主线程执行析构函数时先抢到锁,线程池中的线程会在定义unique_lock上阻塞,进而主线程继续执行通知正在wait的线程,而无论是正在执行任务的线程,还是wait状态的线程,都不会出现死锁问题,因为执行任务的线程不影响,而wait状态的线程会在被唤醒后,也会在if条件下break,正常的被回收
  • 当线程池里面的线程先抢到锁时,主线程线程会阻塞在unique_lock上,线程池里的线程往下执行,无论是执行到wait语句,还是直接获取到任务exec,主程序线程总是会比线程池里的线程慢一步,后执行notify_all()操作来唤醒等待的线程,因此也不会发生死锁问题

三、资源回收机制的改良😦😦😦

前面我们对于线程池的析构函数进行了优化,完善了线程池的资源回收机制,但还是有问题没考虑到,我们资源回收关注的点仅仅是线程,并没有过多的看到任务的具体执行情况,即当线程池要析构、结束时,如果还有用户提交的任务没有执行完,该怎么办 ? 当然,我们很容易想到,既然线程池都已经结束了,那里边正在执行的任务也势必会直接结束。但这仅仅是对我们自己而言,要实现一个完整、通用的系统,我们肯定是需要等待任务执行完毕才行,我们更改以下测试程序,看看会发生什么
在这里插入图片描述
下面是执行结果
在这里插入图片描述
看看,用户如果不需要获取任务执行完毕的返回值(即调用get()方法阻塞),只是让线程池执行任务,他辛辛苦苦提交的任务根本就没有被执行,然而主线程就直接退出了!!!因此我们仍然需要修改我们之前优化过的代码!!!

在这里插入图片描述
图例不太清除,这里就不介绍了,可以在 我的gitee 上下载对应的源码进行对比分析


ubuntu20.04编译动态库g++ threadpool.cpp -fPIC -shared libpool.so -std=c++17
动态库搜索路径/usr/local/lib
头文件搜索路径usr/local/include
测试代码执行命令g++ main.cpp -o a -lpool -lpthread -std=c++17


四、平台差异化的死锁性问题(没有发生,代码竟然能跑🤣🤣)

Visual Stdio 2022 的 condition_variable 底层源码,完善了条件变量的析构函数,正常释放

在这里插入图片描述
Linux系统上 condition_variable 的底层源码,析构函数没有做任何操作
在这里插入图片描述

实际上,我在Linux上运行该项目时,并没有遇到老师提到的这些情况😵😵,这可能是因为我的配置存在问题,或者是因为在后续版本中开发者大佬们解决了这些问题。

五、对应的解决方案🥹🥹🥹

使用Linux环境下的gdb调试,通过gdb attach PID,调试当前正在运行的程序,ps -u可以查看当前正在执行的进程任务,info threads查看所有线程的状态以及当前正在哪个关注线程,thread n关注第n个线程,bt打印该线程执行函数的堆栈信息,进行问题分析。

六、总结😩😩😩

以上内容涉及了《线程池项目(四)》项目中死锁问题的分析以及资源回收机制的改进。关于项目后续采用packaged_task和future进行的二次开发和重构的详情就不介绍了,其源码可在 我的gitee 上找到。这也是本系列《手写线程池》的最后一篇🔚 。未来,我将与大家分享更多项目。我知识有限,博客中还有很多地方需要改进,出现问题希望大佬们能够及时指正,感谢大家的支持!🌻🌻🌻

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

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

相关文章

【Java程序设计】【C00276】基于Springboot的就业信息管理系统(有论文)

基于Springboot的就业信息管理系统(有论文) 项目简介项目获取开发环境项目技术运行截图 项目简介 这是一个基于Springboot的就业信息管理系统 本系统分为前台功能模块、管理员功能模块、学生功能模块、企业功能模块以及导师功能模块。 前台功能模块&…

微服务知识02

1、九大高并发解决方案 2、系统架构图​​​​​​​ 3、分布式事务 本地事务、分布式事务 操作不同服务器的数据库(垂直分库) 4、分布式事务解决方案(没有seata之前) (1)XA协议(强一致性&a…

PixPin:一键搞定截图、长截图、贴图、GIF

名人说:莫道桑榆晚,为霞尚满天。——刘禹锡(刘梦得,诗豪) 创作者:Code_流苏(CSDN)(一个喜欢古诗词和编程的Coder😊) 目录 一、什么是PixPin?①PixPin②核心功…

readproc.h

Ubuntu22.04系统中 编译自己写的程序的时候,报错,显示找不到readproc.h文件,通过安装libprocps-dev解决 sudo apt install libprocps-dev

项目解决方案:街道社区视频监控接入、汇聚和联网设计方案

目 录 一、客户需求 二、网络拓扑图 三、方案描述 四、系统配置 1、服务器配置 2、带宽配置 五、方案优势 1. 平台可堆叠使用 2. 支持主流接入协议 4. 多种终端显示 5. 客户端功能强大 6. 一机一档 一、客户需求 1,一个街道有十个社…

消息中间件篇之RabbitMQ-高可用机制

一、怎么保证高可用性 在生产环境下,使用集群来保证高可用性,一般我们采用普通集群、镜像集群、仲裁队列。 二、普通集群 普通集群,或者叫标准集群(classic cluster),具备下列特征: 1. 会在集…

蓝桥杯DP算法——区间DP(C++)

根据题意要求的是将石子合并的最小权值,我们可以根据DP思想使用二维数组f[i,j]来存放所有从第i堆石子到第j堆石子合并成一堆石子的合并方式。 然后由第二个图所示,我们可以将i到j区间分成两个区间,因为将i到j合并成一个区间的前一步一定是合…

SpringBoot自动装配的原理

废话不多说,直接上干货 SpringBoot 是如何实现自动装配的? 以我这个2.6.13的版本为例 第一步,我们先从SpringBootApplication这个入口注解看起 在springBootApplication这个注解当中有三个关键性的注解,大概可以看作是&#xff1a…

Java面试:Spring Cloud Alibaba

文章目录 引言I Spring Cloud Alibaba1.1 配置文件加载的优先级(由高到低)1.2 注册中心1.3 rpcII 高并发场景:缓存穿透/缓存失效/雪崩如何解决2.1 缓存穿透2.2 缓存击穿(失效)2.3 缓存雪崩引言 微服务涉及的中间件分布式事务事务的传播方式事务的隔离级别缓存穿透/缓存失效…

Matlab/simulink基于vsg的风光储调频系统建模仿真(持续更新)

​ 1.Matlab/simulink基于vsg的风光储调频系统建模仿真(持续更新)

C/C++暴力/枚举/穷举题目持续更新(刷蓝桥杯基础题的进!)

目录 前言 一、百钱买百鸡 二、百元兑钞 三、门牌号码(蓝桥杯真题) 四、相乘(蓝桥杯真题) 五、卡片拼数字(蓝桥杯真题) 六、货物摆放(蓝桥杯真题) 七、最短路径(蓝…

图解目标检测 之 【YOLOv9】 算法 最全原理详解

YOLOv9与SOTA模型对比 什么是 YOLOv9?YOLOv9是YOLO系列中的最新产品,是一种实时目标检测模型。它通过先进的深度学习技术和架构设计,包括通用 ELAN (GELAN) 和可编程梯度信息 (PGI),展现出更好的性能。 YOLO 系列通过引入计算机视…

Java/Python/Go不同开发语言基础数据结构和相关操作总结-GC篇

Java/Python/Go不同开发语言基础数据结构和相关操作总结 1. 常见gc方式1.1 gc判断对象是否存活1.2 引用计数法1.2 标记-清除算法1.3 复制算法1.4 标记-压缩算法1.5 分代收集算法 2. java的gc方式以及垃圾回收器2.1 gc方式2.1 gc回收器2.1.1 Serial收集器2.1.2 ParNew收集器2.1.…

Socket、UDP、TCP协议和简单实现基于UDP的客户端服务端

目录 Socket TCP和UDP区别 UDP:无连接,不可靠传输,面向数据报,全双工 TCP:有连接,可靠传输,面向字节流,全双工 无连接和有连接 可靠传输和不可靠传输 面向数据报和面向字节流…

学习或从事鸿蒙开发工作,有学历要求吗?

目前安卓有2,000万的开发者。本科及以上学历占比为35%;iOS有2,400万开发者,本科及以上学历占比为40% 绝大多数的前端开发者都是大专及以下学历,在2023年华为开发者大会上余承东透露华为的开发者目前有200万,但鸿蒙开发者统计的数据…

C#,数组数据波形排序(Sort in Wave Form)的朴素算法与源代码

1 波形排序 所谓“波形排序”就是一大一小。 将n个身高互不相同的人排成一行 ,对于每个人 ,要求他要么比相邻的人均高 ,要么比相邻的人均矮 ,问共有多少种排法 ,这一问题称为波形排列问题。 2 源程序 using System; using System.Collections; using System.Collections.Gen…

[嵌入式系统-33]:RT-Thread -18- 新手指南:三种不同的版本、三阶段学习路径

目录 前言:学习路径:入门学习-》进阶段学习》应用开发 一、RT-Thread版本 1.1 标准版 1.2 Nano 1.3 Smart版本 1.4 初学者制定学习路线 1.5 RT-Thread在线文档中心目录结构 1.6 学习和使用RT-Thread的三种场景 二、入门学习阶段:内…

信息系统项目管理师论文分享(质量管理)

水一篇文章。我发现身边考高项的朋友很多都是论文没过,我想着那就把我的论文分享出来,希望能有帮助。 质量管理 摘要 2020年5月,我作为项目经理参加了“某市某医联体的互联网诊疗(互联网医院和远程医疗)平台”的建设…

编程的基础:理解时间和空间复杂度

编程的基础:理解时间和空间复杂度 时间复杂度空间复杂度示例常数时间复杂度 O(1)线性时间复杂度 O(n)线性对数时间复杂度 O(n log n)二次时间复杂度 O(n^2)指数时间复杂度 O(2^n) 空间复杂度示例常数空间复杂度 O(1)线性空间复杂度 O(n)线性对数空间复杂度 O(log n)…

apache 模式、优化、功能 与 nginx优化、应用

一、I/O模型——Input/Output模型 1.同步/异步 A程序需要调用B程序的某一个功能,A发送一个请求需要B完成一个任务 同步:B不会主动去通知A是否完成需要A自己去问 异步:B会主动通知A是否完成 2.阻塞/非阻塞 A发送一个请求需要B完成一个任务 …