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

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

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

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

我们先来看看前两种情况

  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 上找到。这也是本系列《手写线程池》的最后一篇🔚 。未来,我将与大家分享更多项目。我知识有限,博客中还有很多地方需要改进,出现问题希望大佬们能够及时指正,感谢大家的支持!🌻🌻🌻

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

leisure-pp

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值