【线程池项目(二)】线程池FIXED模式的实现

本文详细介绍了使用C++面向对象技术实现的线程池项目,涉及ThreadPool、Thread、Semaphore、Any、Task和Result等关键类的设计与关键技术点,包括线程绑定、任务提交与返回值管理。
摘要由CSDN通过智能技术生成

在上一篇【线程池项目(一)】项目介绍和代码展示 中,我们展示了线程池的两个版本实现,它们的代码在具体的实现细节上是优化过了的。下文提供的代码并非完整,也有很多地方尚需改善,但这些差异对理解整个项目而言,影响并不会太大。因此,在接下来的讨论中,我们将重点放在对项目代码的理解上。

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

一、设计思路

使用C++ OOP面向对象编程的思想,设计了6个类:

  • ThreadPool: 负责线程池的创建ThreadPool();、开启start()、设置模式setMode(这里不需要,默认FIXED)、提交任务submitTask()、线程函数threadFunc()(至于为什么要把线程函数放入ThreadPool里,而不是在Thread里,后面会解释)
  • Thread: 负责线程的创建Thread(),定义了函数对象类型ThreadFunc
  • Task: 对外(即用户)提供一个抽象类接口,用户可以重写run()函数来提供相应任务
  • Any: 模仿C++17中的any,可以接收任意的类型
  • Result: 作为Any的依赖,接收task任务在用户提交完成后的返回值类型
  • Semaphore: 线程的通信机制,模仿C++20的Semaphore,用于通知用户提交的任务是否已经被相应的线程执行完毕

二、关键技术点

(1)线程池类ThreadPool

🤠开启线程池start():
当创建线程对象时,我们使用 std::bind() 绑定器将线程池的成员方法 threadFunc 与当前线程池的 this 指针绑定到一起,作为创建线程对象的一个参数,传递给 std::thread 线程对象。这样,在 Thread 类的成员方法中,就可以直接通过调用 ThreadPool 的线程函数 threadFunc 来访问线程池中的成员变量和方法。通过这种绑定方式,也可以解决变参的问题,因为所有相关的参数都已经被绑定到这个线程函数上。因此,我们可以使用别名 using ThreadFunc = std::function<void()> 来定义线程函数的类型。
在这里插入图片描述


🥳提交任务submitTask():
使用互斥锁unique_lock()来维护线程安全,结合条件变量notFull的使用,判断1s内,任务队列中任务的数目是否已达上限,若已达,则阻塞返回提交失败,反之入队,通知其余线程任务队列中有任务能够执行了,返回提交成功任务的返回值Result(sp, true(default)),关于Result见下文
在这里插入图片描述


🥸线程函数threadFunc():
由于线程函数需要获取任务队列里的任务,在获取任务的同时需要线程池中互斥锁和条件变量的管理,直接定义在Thread里不好进行相关操作,定义在全局中也不好访问私有的成员变量,所以把它放在ThreadPool里实现,但是我们的目的是让线程通过线程函数来执行任务,所以我们定义相应的Thread构造函数把ThreadPool的线程函数threadFunc给到Thread,用Thread类的成员变量func_来存储。代码同上,就不赘述了
在这里插入图片描述

(2)线程类Thread

😎 这里没什么可说的,就是一个分离线程detach,延长线程函数的生命周期至主线程结束(即创建线程池的线程)
在这里插入图片描述

(3)信号量类Semaphore

🤓 模拟C++20的semaphore,用waitpost分别申请和释放信号量资源来控制用户提交的任务是否被执行完毕
在这里插入图片描述

(4)上帝类Any

🧐 考虑到用户提交任务时,可能不仅仅需要把任务处理完成,还可能需要得到任务处理完成后的返回值,所以我们设计了一个Any类作为接收任意类型的返回值,即让一个类型可以指向其他的任意类型。然我们知道,基类指针可以执行派生类对象,根据data(存储用户返回的数据,用模板实现)构造出一个派生类对象,这时候我们就可以使用Any里的基类指针指向它,并通过向下转型取出data。这里需要理解一下Any的构造函数,编译器在用户提交的任务执行完返回时是如何处理的
在这里插入图片描述

(5)任务抽象基类Task

😯 由于TaskResult是有耦合的,Result里用智能指针shared_ptr定义的Task类型的成员变量task_,为了避免发生智能指针的交叉引用问题,Task里使用了裸指针定义Result类型的result_,指向用户提交任务后返回的Result,其生命周期 > task, 通过result_Task里面调用Result里的成员方法,来控制Any返回值的获取,在exec()中发生多态调用(run()在基类中为纯虚函数,供用户重写)
在这里插入图片描述

(6)提交任务的返回值Result

😳 构造函数Result(…):
判断提交任务是否成功,并把当前对象this给到成员变量task_里,供Task类中的相关方法使用,如上
在这里插入图片描述


🥹 获取返回值Any ,setVal()、get():
通过信号量的waitpost操作和资源转移std::move()来进行
在这里插入图片描述

三、分析图例,理解函数执行过程

在这里插入图片描述

四、总结

以上就是有关于【线程池项目(二)】线程池FIXED模式的实现的内容,下一篇【线程池项目(三)】介绍线程池fixed模式的具体实现过程🔚🔚🔚

🌻🌻🌻如果聪明的你浏览到这篇文章并觉得文章内容对你有帮助,请不吝动动手指,给博主一个小小的赞和收藏🌻🌻🌻

  • 15
    点赞
  • 31
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

leisure-pp

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

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

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

打赏作者

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

抵扣说明:

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

余额充值