概述
WorkThreadPool管咯一组EventPoller事件轮询器线程,任务通过分配到不同的EventPoller来执行。
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
TaskExecutorGetterImp
TaskExecutorGetterImp管理一组EventPoller对象,实现了简单的负载均衡,每次添加任务时,可以通过getExecutor接口选择一个负载最小的EventPoller来执行任务。
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接口中会开启线程并监听事件。
两个实现与TaskExecutorGetter
因为TaskExecutorGetterImp继承自TaskExecutorGetter,而TaskExecutorGetter定义了两个纯虚接口:
class TaskExecutorGetter {
public:
using Ptr = std::shared_ptr<TaskExecutorGetter>;
virtual ~TaskExecutorGetter() = default;
/**
* 获取任务执行器
* @return 任务执行器
*/
virtual TaskExecutor::Ptr getExecutor() = 0;
/**
* 获取执行器个数
*/
virtual size_t getExecutorSize() const = 0;
};
class TaskExecutorGetterImp : public TaskExecutorGetter {
所以TaskExecutorGetterImp 必须实现这两个接口
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;
}
遍历所有任务执行器,并找到负载最轻的将其返回。
getExecutorSize:获取线程数
size_t TaskExecutorGetterImp::getExecutorSize() const {
return _threads.size();
}
getExecutorDelay:获取所有线程任务执行延时
/**
* 获取所有线程任务执行延时,单位毫秒
* 通过此函数也可以大概知道线程负载情况
* @return
*/
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,执行其自定义的删除器,通过回调将所有线程的任务执行延时返回。
for_each:遍历所有线程
void TaskExecutorGetterImp::for_each(const function<void(const TaskExecutor::Ptr &)> &cb) {
for (auto &th : _threads) {
cb(th);
}
}
getExecutorLoad:获取所有线程的负载率
vector<int> TaskExecutorGetterImp::getExecutorLoad() {
vector<int> vec(_threads.size());
int i = 0;
for (auto &executor : _threads) {
vec[i++] = executor->load();
}
return vec;
}
WorkThreadPool
WorkThreadPool基本上都是使用的其基类TaskExecutorGetterImp提供的方法
- 关于s_pool_size
static size_t s_pool_size = 0;
/**
* 设置EventPoller个数,在WorkThreadPool单例创建前有效
* 在不调用此方法的情况下,默认创建thread::hardware_concurrency()个EventPoller实例
* @param size EventPoller个数,如果为0则为thread::hardware_concurrency()
*/
void WorkThreadPool::setPoolSize(size_t size) {
s_pool_size = size;
}
- 在构造函数中,调用基类的addPoller接口创建并启动了"工作线程池"。
WorkThreadPool::WorkThreadPool() {
//最低优先级
addPoller("work poller", s_pool_size, ThreadPool::PRIORITY_LOWEST, false);
}
- 获取负载任务最轻的那个EventPoller,或者第一个EventPoller
EventPoller::Ptr WorkThreadPool::getFirstPoller() {
return std::dynamic_pointer_cast<EventPoller>(_threads.front());
}
EventPoller::Ptr WorkThreadPool::getPoller() {
return std::dynamic_pointer_cast<EventPoller>(getExecutor());
}
WorkThreadPool和ThreadPool的区别
ThreadPool的“线程池”是一组std::Thread线程对象,池中所有线程共同工作在同一个任务队列上,通过信号量以及锁竟态的争抢任务来执行。
WorkThreadPool的“线程池“是一组EventPoller对象,每一个EventPoller对象内部都启动了一个std::Thread线程,EventPoller是一个事件处理器,在线程中监听指定事件,当事件到来时执行该事件对应的处理回调,默认的EventPoller监听了一个管道事件,对应在管道上有一个任务队列,当添加任务到队列时,触发管道事件,接着从队列中获取任务并执行。也就是说,每一个EventPoller线程都有一个自己的任务队列,多个线程之间不存在竞争关系。并且,还可以根据每一个线程的负载情况,均衡的将任务添加到多个线程中。