cpp-tbox项目链接 https://gitee.com/cpp-master/cpp-tbox
更多精彩内容欢迎关注微信公众号:码农练功房
往期精彩内容:
Linux应用框架cpp-tbox之弱定义
Linux应用框架cpp-tbox之日志系统设计
Linux应用框架cpp-tbox之事件驱动EventLoop
Linux应用框架cpp-tbox之事件驱动Event
整体结构图
下图是线程池的代码模型:
从模型可知,线程池被抽象成了ThreadPool类,其生命周期由ContextImp控制。
ThreadPool依赖于Loop对象,这是为了向IO线程派发任务。
ThreadPool和Data的关系,形如以下代码:
class MyClass {
private:
struct Data; // 声明嵌套的结构体类型
Data* d_; // 声明指针成员,但不在此初始化
public:
MyClass() : d_(nullptr) {} // 在构造函数的成员初始化列表中初始化d_为nullptr
// 其他成员函数、析构函数等...
};
这种写法的好处主要包括以下几点:
- 封装性:通过将Data结构体定义为私有成员,外界无法直接访问Data的内部细节,仅能通过类提供的公共接口来间接操作,增强了数据的封装性和安全性。
- 控制访问与操作隔离:类的设计者可以控制对Data结构体的直接访问和修改仅限于类内部,避免了外部误操作引起的数据不一致性问题。通过成员函数(如getter/setter)来操作d_,可以添加逻辑校验和控制。
- 灵活性与扩展性:将来如果Data结构体需要修改,只需在类内部进行,不会影响到使用该类的外部代码。此外,类可以通过添加新的操作函数来扩展对外接口,而无需更改外部代码对Data的引用方式。
- 内存管理:通过指针d_管理Data实例的生命周期,可以在类的构造函数中分配内存,在析构函数中释放,这样可以更好地控制内存的分配和释放,减少内存泄漏的风险。当然,这也要求开发者需要管理好资源,避免悬挂指针和内存泄露。
- 性能:直接操作指针相比于对象实例,减少了拷贝的开销(尤其是在大型对象或复杂结构体时),可以提高性能。另外,对于某些特定操作,直接操作指针可能提供更大的灵活性和效率。
创建
通过ThreadPool::initialize接口,完成线程池内工作线程的创建。工作线程个数可以在启动配置文件中指定:
{
"thread_pool": {
"max": 5,
"min": 2
}
}
初始化时,按照thread_pool.min个数来创建工作线程。
thread_pool.max为最大创建工作线程个数,如果空闲线程不够分配未认领的任务,且工作线程个数未超最大上限,还可以再创建新的线程。
每个工作线程都会被分配一个ThreadToken作为标识:
using ThreadToken = cabinet::Token;
这里使用using取别名,提高了代码的表现力。
对于cabinet::Token的实现先按下不表,这里只要知道是一个标识就够了,不脱离此次主线。
工作线程创建完成后,使用条件变量让工作线程挂起,直到有要执行的任务为止。
void ThreadPool::threadProc(ThreadToken thread_token)
{
LogDbg("thread %u start", thread_token.id());
while (true) {
Task* item = nullptr;
{