基于前三天的深入分析,第四天的目标是:自己复现整个线程池项目。
首先,我们在Ubuntu虚拟机中创建一个MyThreadPool文件夹:

输入code .
就可以使用vscode打开这个文件夹了
包含了必要的头文件后,我们先声明ThreadPool类
线程池就如同一个流水线工厂,工厂里要有执行任务的工人(线程vector)、任务传送带(任务队列)、提交任务的送货员(enqueue函数)、上下班信号(stop)、中途休息信号(条件变量)。分析完后,我们明确了ThreadPool类的内容。
接着是构造函数与析构函数。在创建工厂后,我们需要立刻招聘工人,所以我们在构造函数中创建好线程,具体多少个由用户自己定义,所以要传一个参数,用来表示创建的线程数量。析构函数发送下班信号,通知左右线程,并等待所有线程做完手头的工作,因此不需要任何参数。
接下来到enqueue函数。用户通过它提交任务(函数),用户具体提交什么函数、它的参数是什么有多少,这个需要等用户具体提交任务的时候才知道。因此,我们不能把这个enqueue函数的参数写死了,使用模板即可解决这个问题。
enqueue函数返回什么?在enqueue中我们提交了任务,提醒任意一个工人来执行任务,然后enqueue函数就结束了。这个时候工人可能还没有执行完任务,所以,这个任务的生命周期必须要超过enqueue函数的生命周期。因此,我们在堆上开辟空间给这个任务,然后用指针管理这片内存。具体是什么指针,后续再说。为了统一,任务队列中的元素都是返回值为void()的无参可调用对象。如何拿到任务执行完的结果?所以,我们希望使用std::future,因此我们可以配合std::packaged_task使用,因为它提供了get_future()接口。这样一来,我们将任务打包,在堆上开辟内存给这个std::packaged_task对象,用指针ptr去管理它,后续这个任务执行完毕后,我们可以通过ptr->get_future()获取和这个std::packaged有共享资源的std::future,这样就可以获取到结果了。
因此,enqueue函数应该返回一个std::future,这个std::future是什么类型的?这个类型就是用户提交的函数返回的类型,具体是什么需要等到用户提交了才知道,但没关系,我们使用了模板。用户提交的函数返回的类型就是std::invoke_result_t<F,Args...>,不需要加typename,因为这本身就是一个类型。为了方便,我们给它起一个别名return_type。所以enqueue函数返回的就是std::future<return_type>对象。但由于这个return_type是编译期才确定的,所以enqueue函数的返回值,我们用auto处理即可。
❓追问:为什么函数f也要完美转发?
答:函数也有左右值之分。
// 1. 函数指针(左值)
void func() {}
enqueue(func); // 左值
// 2. Lambda表达式(左值或右值)
auto lambda = []{};
enqueue(lambda); // 左值
enqueue([]{ return 42; }); // 右值
// 3

最低0.47元/天 解锁文章
990

被折叠的 条评论
为什么被折叠?



