本文关注C++线程池的设计,以一个典型的设计方案为例,分析C++11线程池的设计方式。
设计方案
线程池希望达到的效果:
- 提交任务的方式灵活,可以提交带任意参数的可调用对象。同时提交线程可以获取任务执行完成的状态。
- 线程池伸缩性,自动回收空闲线程
设计思想
基本成员:工作线程队列(workers),任务队列(tasks),以及条件变量。
基本行为:每创建一个工作线程,都等待在任务队列上(获取条件变量)。任务被提交(commit),加入到任务队列后,通知工作线程,工作线程提取任务执行。
提交方式(CommitTask
):为提交带任意参数的可调用对象,使用可变长参数的形式。该函数期望返回异步结果,使用std::future的返回类型,同时在提交之前封装成std::packaged_task的形式从而课获取到std::future类型的结果。
如何回收线程:设计一个监控线程,该线程主要任务是循环监控线程池的空闲线数,在发现空闲线程数超过设置的最大空闲线程数,就回收线程。回收方法很巧妙:向任务队列添加相应数量的“停止任务”,工作线程执行该任务后,会退出线程。
Commit行为
1 | template <typename F, typename... Args> |
设计要点以及使用C++11特性
- 使用模板参数F,以支持任意可调用对象类型的传入,比如lambda,std::function,函数指针,仿函数
- 使用std::future返回任务的执行结果。
- 使用std::packaged_task封装任务,以获取异步结果。
- 使用std::forward,完美转发。
- 使用emplace_back利用了右值引用的特性和完美转发。emplace_back将参数完美转发给vector的构造函数,也就是说,传入的是lambda,则将使用移动构造函数。
工作线程的执行函数
1 | void ThreadPool::_WorkerRoutine() |
监控线程的执行函数
1 | void ThreadPool::_MonitorRoutine() |