系列文章目录
《ZLToolKit源码学习笔记》(1)VS2019源码编译
《ZLToolKit源码学习笔记》(2)工具模块之日志功能分析
《ZLToolKit源码学习笔记》(3)工具模块之终端命令解析
《ZLToolKit源码学习笔记》(4)工具模块之消息广播器
《ZLToolKit源码学习笔记》(6)线程模块之整体框架概述
《ZLToolKit源码学习笔记》(7)线程模块之线程池组件:任务队列与线程组
《ZLToolKit源码学习笔记》(8)线程模块之线程负载计算器
《ZLToolKit源码学习笔记》(9)线程模块之任务执行器
《ZLToolKit源码学习笔记》(11)线程模块之工作线程池WorkThreadPool(本文)
《ZLToolKit源码学习笔记》(12)事件轮询模块之整体框架概述
《ZLToolKit源码学习笔记》(13)事件轮询模块之管道的简单封装
《ZLToolKit源码学习笔记》(14)事件轮询模块之定时器
《ZLToolKit源码学习笔记》(15)事件轮询模块之事件轮询器EventPoller
《ZLToolKit源码学习笔记》(16)网络模块之整体框架概述
《ZLToolKit源码学习笔记》(17)网络模块之基础接口封装类SockUtil
《ZLToolKit源码学习笔记》(18)网络模块之Buffer缓存
《ZLToolKit源码学习笔记》(19)网络模块之套接字封装
《ZLToolKit源码学习笔记》(20)网络模块之TcpServer
《ZLToolKit源码学习笔记》(21)网络模块之TcpClient与Session
《ZLToolKit源码学习笔记》(22)网络模块之UdpServer
前言
WorkThreadPool管理一组EventPoller事件轮询器线程,任务通过分配到不同的EventPoller来执行。
目录
二、WorkThreadpool及其基类TaskExecutorGetterImp
2.1.1、addPoller-创建一组EventPoller对象
2.1.2、getExecutorDelay-获取所有线程任务执行延时
2.1.3、getExecutor-获取负载最轻的任务执行器
三、WorkThreadPool和ThreadPool的区别
一、概述
上图中可以大致看出WorkThreadPool的继承关系:
WorkThreadPool继承自TaskExecutorGettImp,而TaskExecutorGettImp管理了一组EventPoller(继承自TaskExecutor),所以,工作线程池,实际上就是管理了一组EventPoller对象的类。
那为什么要叫线程池呢?我们可以看下EventPoller类的runLoop函数:
void EventPoller::runLoop(bool blocked,bool regist_self) {
if (blocked) {
/* epoll_wait或者select事件监听处理 */
} else {
_loop_thread = new thread(&EventPoller::runLoop, this, true, regist_self);
_sem_run_started.wait();
}
}
可以看到,runLoop函数中,开启了一个线程,所以,每一个EventPoller对象,实际上都持有一个线程。
二、WorkThreadpool及其基类TaskExecutorGetterImp
2.1、TaskExecutorGetterImp
TaskExecutorGetterImp管理一组EventPoller对象,实现了简单的负载均衡,每次添加任务时,可以通过getExecutor接口选择一个负载最小的EventPoller来执行该任务。
2.1.1、addPoller-创建一组EventPoller对象
size_t TaskExecutorGetterImp::addPoller(const string &name, size_t size, int priority, bool register_thread) {
auto cpus = thread::hardware_concurrency();
size = size > 0 ? size : cpus;
for (size_t i = 0; i < size; ++i) {
EventPoller::Ptr poller(new EventPoller((ThreadPool::Priority) priority));
poller->runLoop(false, register_thread);
auto full_name = name + " " + to_string(i);
poller->async([i, cpus, full_name]() {
setThreadName(full_name.data());
setThreadAffinity(i % cpus);
});
_threads.emplace_back(std::move(poller));
}
return size;
}
该接口主要是创建EventPoller对象,根据用户指定的数量来创建,或者根据系统CPU核心数来创建。runLoop接口中会开启线程并监听事件。
2.1.2、getExecutorDelay-获取所有线程任务执行延时
void TaskExecutorGetterImp::getExecutorDelay(const function<void(const vector<int> &)> &callback) {
std::shared_ptr<vector<int> > delay_vec = std::make_shared<vector<int>>(_threads.size());
shared_ptr<void> finished(nullptr, [callback, delay_vec](void *) {
//此析构回调触发时,说明已执行完毕所有async任务
callback((*delay_vec));
});
int index = 0;
for (auto &th : _threads) {
std::shared_ptr<Ticker> delay_ticker = std::make_shared<Ticker>();
th->async([finished, delay_vec, index, delay_ticker]() {
(*delay_vec)[index] = (int) delay_ticker->elapsedTime();
}, false);
++index;
}
}
通过async插入一个用来计时的任务,获取当执行到该任务时所等待的时间。每一个线程添加的任务都捕获了finished智能指针对象,引用计数增加,当所有任务执行完成后,finished引用计数减为0,执行其自定义的删除器,通过回调将所有线程的任务执行延时返回。
2.1.3、getExecutor-获取负载最轻的任务执行器
TaskExecutor::Ptr TaskExecutorGetterImp::getExecutor() {
auto thread_pos = _thread_pos;
if (thread_pos >= _threads.size()) {
thread_pos = 0;
}
TaskExecutor::Ptr executor_min_load = _threads[thread_pos];
auto min_load = executor_min_load->load();
for (size_t i = 0; i < _threads.size(); ++i, ++thread_pos) {
if (thread_pos >= _threads.size()) {
thread_pos = 0;
}
auto th = _threads[thread_pos];
auto load = th->load();
if (load < min_load) {
min_load = load;
executor_min_load = th;
}
if (min_load == 0) {
break;
}
}
_thread_pos = thread_pos;
return executor_min_load;
}
遍历所有任务执行器,并找到负载最轻的将其返回。
2.2、WorkThreadPool
WorkThreadPool基本上都是使用的其基类TaskExecutorGetterImp提供的方法,在构造函数中,调用基类的addPoller接口创建并启动了"工作线程池"。
EventPoller::Ptr WorkThreadPool::getFirstPoller(){
return dynamic_pointer_cast<EventPoller>(_threads.front());
}
EventPoller::Ptr WorkThreadPool::getPoller(){
return dynamic_pointer_cast<EventPoller>(getExecutor());
}
WorkThreadPool::WorkThreadPool(){
//最低优先级
addPoller("work poller", s_pool_size, ThreadPool::PRIORITY_LOWEST, false);
}
void WorkThreadPool::setPoolSize(size_t size) {
s_pool_size = size;
}
三、WorkThreadPool和ThreadPool的区别
ThreadPool的“线程池”是一组std::Thread线程对象,池中所有线程共同工作在同一个任务队列上,通过信号量以及锁竟态的争抢任务来执行。
WorkThreadPool的“线程池“是一组EventPoller对象,每一个EventPoller对象内部都启动了一个std::Thread线程,EventPoller是一个事件处理器,在线程中监听指定事件,当事件到来时执行该事件对应的处理回调,默认的EventPoller监听了一个管道事件,对应在管道上有一个任务队列,当添加任务到队列时,触发管道事件,接着从队列中获取任务并执行。也就是说,每一个EventPoller线程都有一个自己的任务队列,多个线程之间不存在竞争关系。并且,还可以根据每一个线程的负载情况,均衡的将任务添加到多个线程中。