ZLToolKit源码阅读:工作线程池WorkThreadPool

1059 篇文章 277 订阅

概述

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线程都有一个自己的任务队列,多个线程之间不存在竞争关系。并且,还可以根据每一个线程的负载情况,均衡的将任务添加到多个线程中。

  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值