手把手教你写一个线程池(C++11)

手把手教你写一个线程池(C++11)

本文章会尽可能详细的为读者介绍线程池的原理,剖析我们该如何构建一个线程池。文中示意插图均为作者手绘。本文会用到C++11提供的多线程库,参考C++网站https://cplusplus.com/reference/multithreading/

线程池简介

线程池(thread pool):一种线程使用模式。线程过多会带来调度开销,进而影响缓存局部性和整体性能。而线程池维护着多个线程,等待着监督管理者分配可并发执行的任务。这避免了在处理短时间任务时创建与销毁线程的代价。线程池不仅能够保证内核的充分利用,还能防止过分调度。
关于线程池的好处在此处不再赘述。下面我们来思考,一个线程池由哪些部分组成呢?线程池的运行逻辑又是怎么样的呢?
线程池

线程池类基础对象

首先,让我们从线程池的功能为起点思考,线程池是啥?通俗点说,线程池是一堆已经创建好的线程,等待着任务到来而处理任务。

  1. 因此,线程池需要一个线程的集合(图中中部列表)。
  2. 其次,需要一个任务列表(图中右部列表),来存储丢入线程池中的任务。而工作的线程将持续地去获取任务列表中地任务来处理。
  3. 同时,考虑到由于线程的数量需要约束,需要有一个管理者线程(Manager)来管理池内工作线程的增删。如果任务太多,核心线程(蓝色圆框)数量不够,线程可以适当增加(绿色圆圈,代表外加的非核心线程);如果有大量线程处于空闲状态,那我们就要删除一些无用的线程。
  4. 考虑到多线程的编程访问共享内存,我们需要一把锁来约束各线程。同时,为了起到线程拿取不到任务时阻塞等待的效果,我们需要一个条件变量cond和一个对应的互斥锁mutex。而我们可以复用此互斥锁作为线程之间的锁。
  5. 我们还需要一个原子变量atomic来作为我们判断线程的任务是否全部完成的标志。
  6. 最后,我们需要一个flag来存储线程池的状态,判断是否要退出。
    综上,在class threadpool中,我们可以大致构建出我们线程池需要的对象:
//线程池状态枚举类型
typedef enum{
        EXSIT = 0,
        QUIT
    }THREADPOOL_STATUS;


class threadpool{
/*
成员列表
*/
protected:
///@brief 任务队列相关

    //队列内任务结点类型
    struct TaskFunc{
        TaskFunc(uint64_t extime):_extime(extime){}
        int64_t _extime; //任务超时时间,
        std::function<void()> _func;
    };
    typedef std::shared_ptr<TaskFunc> TaskFunc_ptr; 

    //任务队列
    std::queue<TaskFunc_ptr> _Task_queue;

    //任务队列上限
    const size_t MAXTASKNUM = 10; 

///@brief 线程相关

    //线程个数
    size_t _min_thread_num;
    size_t _max_thread_num;
    size_t _busy_thread_num = 0;
    size_t _alive_thread_num = 0;
    size_t _destroy_thread_num = 0;

    //线程组
    std::vector<std::thread*> _threads;

    //管理者线程
    std::thread* _manager = nullptr;

///@brief 条件变量 && 锁

    //线程用 变量 && 锁
    std::mutex _mutex;
    std::condition_variable  _cond;

    //原子变量
    std::atomic<int> _atomic{0};


///@brief 线程池状态变量
    volatile THREADPOOL_STATUS _status; 
}

下面来逐个解释具体作用:

  1. 任务队列相关

    • TaskFunc是我们对“任务”做的第二层封装,也是最后的封装。有关Task的整体封装逻辑,详见后文的AddTask函数。
    • _Task_queue是任务队列,每个结点是一个指向TaskFunc的指针。
    • MAXTASKNUM是逻辑上我们设定的任务队列可以容存的最大值。一旦加入的任务数量超过了这个值,我们理应触发拒绝策略处理任务。本文为了明显的显示线程添加策略,未指定拒绝策略,并将MAXTASKNUM的值默认定为了10了。
  2. 线程组相关

    • _min_thread_num 线程池最小线程数

    • _max_thread_num 线程池最大线程数

    • _busy_thread_num 线程池中正在工作的线程数

    • _alive_thread_num 线程池中存活的线程数

    • _destroy_thread_num 线程池中应该销毁的线程数

    • _threads 工作线程集合

    • _manager 管理者线程

  3. 锁、条件变量、原子变量 (没啥好介绍的 略)

  4. _status 代表线程池状态 枚举类型有QUIT代表要退出 EXSIT代表正常存在

线程池函数

首先来看看线程池的大致流程:
在这里插入图片描述

  1. 构造 && 析构
/*
PUBLIC API
*/
public:
    /**
     * @brief 构造函数 初始化状态值
     * @param min max 用来初始化 _min_thread_num和_max_thread_num
     */
    threadpool(size_t min,size_t max):_min_thread_num(min),_max_thread_num(max),_status(EXSIT){};
    
    /**
     * @brief 析构 stop线程池 
     * 
     */
    virtual ~threadpool(){threadpool_Stop();}
  1. 公共api

  • 首先提供一系列供用户拿取线程数量的api
 /**
     * @brief 获取 各个种类线程个数
     * 
     * @return size_t  线程个数
     */
    size_t threadpool_GetAliveSize(){std::unique_lock<std::mutex> lck(_mutex); return this->_alive_thread_num;}

    size_t threadpool_GetDestroySize(){std::unique_lock<std::mutex> lck(_mutex);return this->_destroy_thread_num;}

    size_t threadpool_GetMinSize(){std::unique_lock<std::mutex> lck(_mutex);return  this->_min_thread_num;}

    size_t threadpool_GetMaxSize(){std::unique_lock<std::mutex> lck(_mutex);return  this->_max_thread_num;}

    size_t threadpool_GetBusySize(){std::unique_lock<std::mutex> lck(_mutex);return  this->_busy_thread_num;}
    /**
     * @brief 获取线程池状态
     * 
     * @return THREADPOOL_STATUS 
     */
    THREADPOOL_STATUS threadpool_GetStatus(){return this->_status;}
  • 线程池运行逻辑api
/**
     * @brief 初始化线程个数(用于线程池实例创建后 且 线程池start之前 以改变参数min和max大小)
     * 
     * @param min 设置线程最小数量
     * @param max 设置线程最大数量
     * @return true 成功设置
     * @return false 
     */
    bool threadpool_InitSize(size_t min,size_t max);

    /**
     * @brief 线程池开始运行
     * 
     * @return int 0 --> 正常创建线程并运行  !0 --> 出错
     */
    int threadpool_Start();

    /**
     * @brief AddTask 往线程池加入任务 
     * 
     * @tparam F 函数名
     * @tparam Args 函数的可变参数列表
     * @param func 函数名
     * @param args 函数名的可变参数列表
     * @return std::future<decltype(func(args...))> 一个future 返回值能用于异步操作
     */
    template <class F,class... Args>
    auto threadpool_AddTask(F&& func,Args... args) -> std::future<decltype(func(args...))>{return this->threadpool_AddTask(0,func,args...);}

    template <class F,class... Args>
    auto threadpool_AddTask(uint16_t extime,F&& func,Args... args) -> std::future<decltype(func(args...))>;

    /**
     * @brief 等待所有线程返回
     * 
     * @param extime 等待时间(单位毫秒 ms) 超时直接返回 / 若为非正数则表永远等待
     * @return true 成功返回
     */
    bool threadpool_WaitEnd(int extime = 0);

    /**
     * @brief stop 等待所有线程返回并销毁线程池
     * 
     */
    void threadpool_Stop();

相应实现:

bool threadpool::threadpool_InitSize(size_t min,size_t max)
{
    std::unique_lock<std::mutex> lck(_mutex);   //拿到锁

    if(!_threads.empty()){
        std::cout<<"error: 线程池已在工作!"<<std::endl;
        return false;
    }
    
    _min_thread_num=min;
    _max_thread_num=max;
    return true;
}

int threadpool::threadpool_Start()
{
    std::unique_lock<std::mutex>lck(_mutex);	//拿到锁
    if(!_threads.empty()){
        std::cout<<"error: 线程池已在工作!"<<std::endl;
        return 1;
    }
	
	//创建管理者线程
    _manager=new std::thread(&threadpool::manager_callback,this);
    std::cout<<"Manager thread "<<_manager->get_id()<<" is created!"<<std::endl;
	
	//创建_min_thread_num个工作线程
    for(int i=0;i<_min_thread_num;i++){
        _threads.push_back(new std::thread(&threadpool::thread_callback,this));
        std::cout<<"(Init)thread "<<_threads[i]->get_id()<<" is created!"<<std::endl;
        _alive_thread_num++;
    }

    return 0;
}

bool threadpool::threadpool_WaitEnd(int extime)
{
    std::unique_lock<std::mutex> lck(_mutex);
    //_atomic==0代表所有任务都已经完成(见工作线程工作函数thread_callback中的操作)
    //_Task_queue.empty()表示任务被取完
	
	//如果任务都被拿完且目前被取的任务都完成了,说明线程池已经空了,所有线程都在休眠 满足要求 直接返回
    if(_Task_queue.empty() && _atomic == 0)return true;
	
	//如果不满足要求 仍有任务未完成
	//则按照传入的extime超时时间,进行条件变量的等待
    if(extime <= 0) _cond.wait(lck,[this]{return _Task_queue.empty() && _atomic == 0;});
    else _cond.wait_for(lck,std::chrono::milliseconds(extime),[this]{return _Task_queue.empty() && _atomic == 0;});

    return true;
}

void threadpool::threadpool_Stop()
{
	//设置退出状态 并通知所有休眠线程
    {
        std::unique_lock<std::mutex> lck(_mutex);

        _status = QUIT;

        _cond.notify_all();
    }
    
    //此处等待所有线程返回 必须释放锁
    if(_manager->joinable())_manager->join();
    delete _manager;
    _manager=nullptr;

    for(int i=0;i<_threads.size();i++){
        if(_threads[i]->joinable())_threads[i]->join();
        delete _threads[i];
        _threads[i]=nullptr;
    }

    

    std::unique_lock<std::mutex> lck(_mutex);
    _threads.clear();
}

//这里语法有点晦涩难懂 请跟随注释逐步分析
//由于我们根据传入的func和args来确定返回值,因此我们无法像一般函数一样直接确定返回值。
//auto fun(args..) -> A 代表我们声明一个返回类型为A的函数fun
//这里 我们的返回值是一个future类,并且类模板为func(args...)的返回值。decltype()用来获取一个函数的返回值 
template <class F, class... Args>
auto threadpool::threadpool_AddTask(uint16_t extime, F &&func, Args... args) -> std::future<decltype(func(args...))>
{
    int64_t expireTime =  (extime == 0 ? 0 : TNOWMS + extime);  // 获取现在时间
    
    //开始封装任务
    using RetType = decltype(func(args...));	//获取func的返回值
    //将函数func和可变参数列表args...首先通过bind()绑定为一个函数F(bind返回的是一个函数),这个函数F的类型为decltype(func(args...))(),即RetType();
    //然后将这个封装了一次的函数F,再封装成一个packaged_task,这样我们就可以避免arg...不定参数集的不确定性
    //最后 我们返回一个指向该packaged_task的指针packaged_tk指针 
    //封装示意:func(args...) == packaged_task() == (*packaged_tk)()
    auto packaged_tk=std::make_shared<std::packaged_task<RetType()>>(std::bind(std::forward<F>(func),std::forward<Args>(args)...));
	
	//最后的封装,将func(arg...)封装成一个void()类型函数。并设定超时时间
    TaskFunc_ptr tsk =std::make_shared<TaskFunc>(expireTime);
    tsk->_func=[packaged_tk]{
        (*packaged_tk)();
    };
	
	//拿锁,访问任务队列,加入任务。并通知线程该醒了
    std::unique_lock<std::mutex> lck(_mutex);
    _Task_queue.push(tsk);
    _cond.notify_all();
	
	//返回一个future类型 方便异步获取
    return packaged_tk->get_future();
}
  • 两个线程工作函数
protected:
    /**
     * @brief 从任务列表试图抓取一个任务
     * 
     * @param task 返回抓取的任务
     * @return true 抓取成功
     * @return false 抓取失败! 线程池退出
     */
    bool gettask(TaskFunc_ptr &task);

    /**
     * @brief 线程工作函数
     * 
     */
    void thread_callback();

    /**
     * @brief 管理者工作函数
     * 
     */
    void manager_callback();

实现:

bool threadpool::gettask(TaskFunc_ptr &task)
{
    std::unique_lock<std::mutex> lck(_mutex);
    //如果任务队列为空 则线程阻塞等待
    if(_Task_queue.empty()){
        _cond.wait(lck,[this]{
                return this->_status == QUIT || !this->_Task_queue.empty() || this->_destroy_thread_num != 0;
            });
    }
    
    //如果管理者判断要销毁线程
    if(_destroy_thread_num !=0){
        _destroy_thread_num -- ;
        _alive_thread_num -- ;
        return false;
    }

    //如果要退出了  就不允许继续拿取任务 返回false
    if(this->_status == QUIT)return false;

    //否则 一切正常 拿取任务
    if(!_Task_queue.empty()){
        task=std::move(_Task_queue.front());    //避免拷贝
        _Task_queue.pop();
        return true;
    }
    return true;
}

void threadpool::thread_callback()
{
	//只要线程池不退出 线程就一直循环工作
    while(this->_status == EXSIT){
        TaskFunc_ptr task;
        int ret=gettask(task); //尝试拿取任务
        if(ret){
            //如果正常抓取到了任务
            std::unique_lock<std::mutex> lck(_mutex);
            _busy_thread_num++;	//线程入busy状态
            lck.unlock();

            ++_atomic;	//_atomic表示一个任务被取出且被开始执行

            try
            {
                if(task->_extime > 0 && task->_extime < TNOWMS){
                    //任务超时
                }
                else {
                    task->_func();
                }
            }
            catch(...)
            {
                std::cout<<"任务执行出错!"<<std::endl;
            }
            
            --_atomic;	//--_atomic 表示一个被取出的任务被完成了

            lck.lock();
            _busy_thread_num--;
            lck.unlock();

            lck.lock();
            //线程取出任务后,需要完成任务。有可能完成任务很费时间。
            //因此 只有所有任务全部被取出(任务队列为空) 且所有被取出的任务都被完成(_atomic == 0) 才能说明所有线程完成任务
            if(_atomic == 0 && _Task_queue.empty()){
                //如果任务队列为空 且 所有任务都执行完成 则给wait函数广播发送信号
                _cond.notify_all();
            }
        } else {
            std::unique_lock<std::mutex> lck(_mutex);
            std::cout<<"Thread "<<std::this_thread::get_id()<<" is Killed!"<<std::endl;
            return;
        }
    }
    std::cout<<"Thread "<<std::this_thread::get_id()<<" is Exited!"<<std::endl;
    return ;
}

void threadpool::manager_callback()
{
    //每隔2秒检测一次
    while(this->_status != QUIT){

        std::this_thread::sleep_for(std::chrono::seconds(2));
        
        std::unique_lock<std::mutex> lck(_mutex);

        //增加线程策略 如果任务列表超了并且线程数量未达上限
        if(_Task_queue.size() >= MAXTASKNUM && _alive_thread_num<_max_thread_num){
            for(int i=0;i<=0 && _alive_thread_num < _max_thread_num;i++){
                _threads.push_back(new std::thread(thread_callback,this));
                _alive_thread_num++;
                std::cout<<"thread "<<_threads[_alive_thread_num-1]->get_id()<<"is created!"<<std::endl;
                
            }
        }

        //删除线程策略
        if(_alive_thread_num > _min_thread_num && 2*_busy_thread_num <_alive_thread_num){
            _destroy_thread_num ++;
            _cond.notify_all();
        }
    }
}

总代码 && 测试

threadpool.h

#ifndef THREADPOOL_CPP
#define THREADPOOL_CPP

#include <iostream>
#include <functional>
#include <future>
#include <thread>
#include <mutex>
#include <queue>
#include <memory>
#include <vector>
#include <Windows.h>

#define TNOW      getNow()
#define TNOWMS    getNowMs()

/**
 * @brief 用来获取当前时间
 * 
 * @param tv 
 */
void getNow(timeval *tv);
int64_t getNowMs();
int gettimeofday(struct timeval &tv);

typedef enum{
        EXSIT = 0,
        QUIT
    }THREADPOOL_STATUS;

class threadpool{
/*
成员列表
*/
protected:
///@brief 任务队列相关

    //队列内任务结点类型
    struct TaskFunc{
        TaskFunc(uint64_t extime):_extime(extime){}
        int64_t _extime; //任务超时时间,
        std::function<void()> _func;
    };
    typedef std::shared_ptr<TaskFunc> TaskFunc_ptr; 

    //任务队列
    std::queue<TaskFunc_ptr> _Task_queue;

    //任务队列上限
    const size_t MAXTASKNUM = 10; 

///@brief 线程相关

    //线程个数
    size_t _min_thread_num;
    size_t _max_thread_num;
    size_t _busy_thread_num = 0;
    size_t _alive_thread_num = 0;
    size_t _destroy_thread_num = 0;

    //线程组
    std::vector<std::thread*> _threads;

    //管理者线程
    std::thread* _manager = nullptr;

///@brief 条件变量 && 锁

    //线程用 变量 && 锁
    std::mutex _mutex;
    std::condition_variable  _cond;

    //原子变量
    std::atomic<int> _atomic{0};


///@brief 线程池状态变量
    volatile THREADPOOL_STATUS _status; 


/*
PUBLIC API
*/
public:
    /**
     * @brief 构造函数 初始化状态值
     * 
     */
    threadpool(size_t min,size_t max):_min_thread_num(min),_max_thread_num(max),_status(EXSIT){};
    
    /**
     * @brief 析构 stop线程池 
     * 
     */
    virtual ~threadpool(){threadpool_Stop();}

    /**
     * @brief 获取 各个种类线程个数
     * 
     * @return size_t  线程个数
     */
    size_t threadpool_GetAliveSize(){std::unique_lock<std::mutex> lck(_mutex); return this->_alive_thread_num;}

    size_t threadpool_GetDestroySize(){std::unique_lock<std::mutex> lck(_mutex);return this->_destroy_thread_num;}

    size_t threadpool_GetMinSize(){std::unique_lock<std::mutex> lck(_mutex);return  this->_min_thread_num;}

    size_t threadpool_GetMaxSize(){std::unique_lock<std::mutex> lck(_mutex);return  this->_max_thread_num;}

    size_t threadpool_GetBusySize(){std::unique_lock<std::mutex> lck(_mutex);return  this->_busy_thread_num;}
    /**
     * @brief 获取线程池状态
     * 
     * @return THREADPOOL_STATUS 
     */
    THREADPOOL_STATUS threadpool_GetStatus(){return this->_status;}

    /**
     * @brief 初始化线程个数
     * 
     * @param min 设置线程最小数量
     * @param max 设置线程最大数量
     * @return true 成功设置
     * @return false 
     */
    bool threadpool_InitSize(size_t min,size_t max);

    /**
     * @brief 线程池开始运行
     * 
     * @return int 0 --> 正常创建线程并运行  !0 --> 出错
     */
    int threadpool_Start();

    /**
     * @brief AddTask 往线程池加入任务 
     * 
     * @tparam F 函数名
     * @tparam Args 函数的可变参数列表
     * @param func 函数名
     * @param args 函数名的可变参数列表
     * @return std::future<decltype(func(args...))> 一个future 返回值能用于异步操作
     */
    template <class F,class... Args>
    auto threadpool_AddTask(F&& func,Args... args) -> std::future<decltype(func(args...))>{return this->threadpool_AddTask(0,func,args...);}

    template <class F,class... Args>
    auto threadpool_AddTask(uint16_t extime,F&& func,Args... args) -> std::future<decltype(func(args...))>;

    /**
     * @brief 等待所有线程返回
     * 
     * @param extime 等待时间(单位毫秒 ms) 超时直接返回 / 若为非正数则表永远等待
     * @return true 成功返回
     */
    bool threadpool_WaitEnd(int extime = 0);

    /**
     * @brief stop 等待所有线程返回并销毁线程池
     * 
     */
    void threadpool_Stop();


protected:
    /**
     * @brief 从任务列表试图抓取一个任务
     * 
     * @param task 返回抓取的任务
     * @return true 抓取成功
     * @return false 抓取失败! 线程池退出
     */
    bool gettask(TaskFunc_ptr &task);

    /**
     * @brief 线程工作函数
     * 
     */
    void thread_callback();

    /**
     * @brief 管理者工作函数
     * 
     */
    void manager_callback();
};


template <class F, class... Args>
auto threadpool::threadpool_AddTask(uint16_t extime, F &&func, Args... args) -> std::future<decltype(func(args...))>
{
    int64_t expireTime =  (extime == 0 ? 0 : TNOWMS + extime);  // 获取现在时间
    using RetType = decltype(func(args...));
    auto packaged_tk=std::make_shared<std::packaged_task<RetType()>>(std::bind(std::forward<F>(func),std::forward<Args>(args)...));

    TaskFunc_ptr tsk =std::make_shared<TaskFunc>(expireTime);
    tsk->_func=[packaged_tk]{
        (*packaged_tk)();
    };

    std::unique_lock<std::mutex> lck(_mutex);
    _Task_queue.push(tsk);
    _cond.notify_all();

    return packaged_tk->get_future();
}

#endif




threadpool.cpp

#include "threadpool.h"

using namespace std;


bool threadpool::threadpool_InitSize(size_t min,size_t max)
{
    std::unique_lock<std::mutex> lck(_mutex);   //拿到锁

    if(!_threads.empty()){
        std::cout<<"error: 线程池已在工作!"<<std::endl;
        return false;
    }
    
    _min_thread_num=min;
    _max_thread_num=max;
    return true;
}

int threadpool::threadpool_Start()
{
    std::unique_lock<std::mutex>lck(_mutex);
    if(!_threads.empty()){
        std::cout<<"error: 线程池已在工作!"<<std::endl;
        return 1;
    }

    _manager=new std::thread(&threadpool::manager_callback,this);
    std::cout<<"Manager thread "<<_manager->get_id()<<" is created!"<<std::endl;

    for(int i=0;i<_min_thread_num;i++){
        _threads.push_back(new std::thread(&threadpool::thread_callback,this));
        std::cout<<"(Init)thread "<<_threads[i]->get_id()<<" is created!"<<std::endl;
        _alive_thread_num++;
    }

    return 0;
}

bool threadpool::threadpool_WaitEnd(int extime)
{
    std::unique_lock<std::mutex> lck(_mutex);
    if(_Task_queue.empty() && _atomic == 0)return true;

    if(extime <= 0) _cond.wait(lck,[this]{return _Task_queue.empty() && _atomic == 0;});
    else _cond.wait_for(lck,std::chrono::milliseconds(extime),[this]{return _Task_queue.empty() && _atomic == 0;});

    return true;
}

void threadpool::threadpool_Stop()
{
    {
        std::unique_lock<std::mutex> lck(_mutex);

        _status = QUIT;

        _cond.notify_all();
    }
    
    //此处等待所有线程返回 必须释放锁
    if(_manager->joinable())_manager->join();
    delete _manager;
    _manager=nullptr;

    for(int i=0;i<_threads.size();i++){
        if(_threads[i]->joinable())_threads[i]->join();
        delete _threads[i];
        _threads[i]=nullptr;
    }

    

    std::unique_lock<std::mutex> lck(_mutex);
    _threads.clear();
}

bool threadpool::gettask(TaskFunc_ptr &task)
{
    std::unique_lock<std::mutex> lck(_mutex);
    //如果任务队列为空 则线程阻塞等待
    if(_Task_queue.empty()){
        _cond.wait(lck,[this]{
                return this->_status == QUIT || !this->_Task_queue.empty() || this->_destroy_thread_num != 0;
            });
    }
    
    //如果管理者判断要销毁线程
    if(_destroy_thread_num !=0){
        _destroy_thread_num -- ;
        _alive_thread_num -- ;
        return false;
    }

    //如果要退出了  就不允许继续拿取任务 返回false
    if(this->_status == QUIT)return false;

    //否则 一切正常 拿取任务
    if(!_Task_queue.empty()){
        task=std::move(_Task_queue.front());    //避免拷贝
        _Task_queue.pop();
        return true;
    }
    return true;
}

void threadpool::thread_callback()
{
    while(this->_status == EXSIT){
        TaskFunc_ptr task;
        int ret=gettask(task);
        if(ret){
            //如果正常抓取到了任务
            std::unique_lock<std::mutex> lck(_mutex);
            _busy_thread_num++;
            lck.unlock();

            ++_atomic;

            try
            {
                if(task->_extime > 0 && task->_extime < TNOWMS){
                    //任务超时
                }
                else {
                    task->_func();
                }
            }
            catch(...)
            {
                std::cout<<"任务执行出错!"<<std::endl;
            }
            
            --_atomic;

            lck.lock();
            _busy_thread_num--;
            lck.unlock();

            lck.lock();
            if(_atomic == 0 && _Task_queue.empty()){
                //如果任务队列为空 且 所有任务都执行完成 则给wait函数广播发送信号
                _cond.notify_all();
            }
        } else {
            std::unique_lock<std::mutex> lck(_mutex);
            std::cout<<"Thread "<<std::this_thread::get_id()<<" is Killed!"<<std::endl;
            return;
        }
    }
    std::cout<<"Thread "<<std::this_thread::get_id()<<" is Exited!"<<std::endl;
    return ;
}

void threadpool::manager_callback()
{
    //每隔2秒检测一次
    while(this->_status != QUIT){

        std::this_thread::sleep_for(std::chrono::seconds(2));
        
        std::unique_lock<std::mutex> lck(_mutex);

        //增加线程策略
        if(_Task_queue.size() >= MAXTASKNUM && _alive_thread_num<_max_thread_num){
            for(int i=0;i<=0 && _alive_thread_num < _max_thread_num;i++){
                _threads.push_back(new std::thread(thread_callback,this));
                _alive_thread_num++;
                std::cout<<"thread "<<_threads[_alive_thread_num-1]->get_id()<<"is created!"<<std::endl;
                
            }
        }

        //删除线程策略
        if(_alive_thread_num > _min_thread_num && 2*_busy_thread_num <_alive_thread_num){
            _destroy_thread_num ++;
            _cond.notify_all();
        }
    }
}

int gettimeofday(struct timeval &tv)
{
#if WIN32
    time_t clock;
    struct tm tm;
    SYSTEMTIME wtm;
    GetLocalTime(&wtm);
    tm.tm_year   = wtm.wYear - 1900;
    tm.tm_mon   = wtm.wMonth - 1;
    tm.tm_mday   = wtm.wDay;
    tm.tm_hour   = wtm.wHour;
    tm.tm_min   = wtm.wMinute;
    tm.tm_sec   = wtm.wSecond;
    tm. tm_isdst  = -1;
    clock = mktime(&tm);
    tv.tv_sec = clock;
    tv.tv_usec = wtm.wMilliseconds * 1000;

    return 0;
#else
    return ::gettimeofday(&tv, 0);
#endif
}

void getNow(timeval *tv)
{
#if TARGET_PLATFORM_IOS || TARGET_PLATFORM_LINUX

    int idx = _buf_idx;
    *tv = _t[idx];
    if(fabs(_cpu_cycle - 0) < 0.0001 && _use_tsc)
    {
        addTimeOffset(*tv, idx);
    }
    else
    {
        TC_Common::gettimeofday(*tv);
    }
#else
    gettimeofday(*tv);
#endif
}

int64_t getNowMs()
{
    struct timeval tv;
    getNow(&tv);

    return tv.tv_sec * (int64_t)1000 + tv.tv_usec / 1000;
}


测试代码:
test.cpp

#include <iostream>
#include "threadpool.h"
#include <memory>
using namespace std;

int count=0;
mutex mut;

void task(){

    for(int i=1;i<=10;i++){
        this_thread::sleep_for(chrono::milliseconds(1000));
        unique_lock<mutex> lck(mut);
        count++;
        cout<<" count = "<<count<<endl;
    }

}

void do_other_things(){
    this_thread::sleep_for(chrono::seconds(10));
    cout<<"do other things!"<<endl;
}

int main(){
    threadpool mypool(8,100);
    cout<<mypool.threadpool_GetAliveSize()<<endl;
    mypool.threadpool_Start();
    cout<<mypool.threadpool_GetAliveSize()<<endl;

    for(int i=0;i<20;i++)
    mypool.threadpool_AddTask(&task);

    mypool.threadpool_WaitEnd(-1);

    cout<<"here"<<endl;
    mypool.threadpool_AddTask(&do_other_things);

    mypool.threadpool_WaitEnd(-1);
    mypool.threadpool_Stop();
    return 0;
}

测试结果:

0
Manager thread 2 is created!
(Init)thread 3 is created!
(Init)thread 4 is created!
(Init)thread 5 is created!
(Init)thread 6 is created!
(Init)thread 7 is created!
(Init)thread 8 is created!
(Init)thread 9 is created!
(Init)thread 10 is created!
8
 count = 1
 count = 2
 count = 3
 count = 4
 count = 5
 count = 6
 count = 7
 count = 8
 count = 9
thread 11is created!
 count = 10
 count = 11
 count = 12
 count = 13
 count = 14
 count = 15
 count = 16
 count = 17
 count = 18
 count = 19
 count = 20
 count = 21
 count = 22
 count = 23
 count = 24
 count = 25
 count = 26
 count = 27
thread 12is created!
 count = 28
 count = 29
 count = 30
 count = 31
 count = 32
 count = 33
 count = 34
 count = 35
 count = 36
 count = 37
 count = 38
 count = 39
 count = 40
 count = 41
 count = 42
 count = 43
 count = 44
 count = 45
thread 13is created!
 count = 46
 count = 47
 count = 48
 count = 49
 count = 50
 count = 51
 count = 52
 count = 53
 count = 54
 count = 55
 count = 56
 count = 57
 count = 58
 count = 59
 count = 60
 count = 61
 count = 62
 count = 63
 count = 64
 count = 65
 count = 66
 count = 67
 count = 68
 count = 69
 count = 70
 count = 71
 count = 72
 count = 73
 count = 74
 count = 75
 count = 76
 count = 77
 count = 78
 count = 79
 count = 80
 count = 81
 count = 82
 count = 83
 count = 84
 count = 85
 count = 86
 count = 87
 count = 88
 count = 89
 count = 90
 count = 91
 count = 92
 count = 93
 count = 94
 count = 95
 count = 96
 count = 97
 count = 98
 count = 99
 count = 100
 count = 101
 count = 102
 count = 103
 count = 104
 count = 105
 count = 106
 count = 107
 count = 108
 count = 109
 count = 110
 count = 111
 count = 112
 count = 113
 count = 114
 count = 115
 count = 116
 count = 117
 count = 118
 count = 119
 count = 120
 count = 121
 count = 122
 count = 123
 count = 124
 count = 125
 count = 126
 count = 127
 count = 128
 count = 129
 count = 130
 count = 131
 count = 132
 count = 133
 count = 134
 count = 135
 count = 136
 count = 137
 count = 138
 count = 139
 count = 140
 count = 141
 count = 142
 count = 143
 count = 144
 count = 145
 count = 146
 count = 147
 count = 148
 count = 149
 count = 150
 count = 151
 count = 152
 count = 153
 count = 154
 count = 155
 count = 156
 count = 157
 count = 158
 count = 159
 count = 160
 count = 161
 count = 162
 count = 163
 count = 164
 count = 165
 count = 166
 count = 167
 count = 168
 count = 169
 count = 170
 count = 171
 count = 172
 count = 173
 count = 174
 count = 175
 count = 176
 count = 177
 count = 178
 count = 179
 count = 180
 count = 181
 count = 182
 count = 183
 count = 184
 count = 185
 count = 186
 count = 187
 count = 188
 count = 189
 count = 190
 count = 191
 count = 192
 count = 193
 count = 194
 count = 195
 count = 196
 count = 197
 count = 198
 count = 199
Thread 6 is Killed!
 count = 200
here
Thread 4 is Killed!
Thread 3 is Killed!
do other things!
Thread 11 is Killed!
Thread 5 is Killed!
Thread 9 is Killed!
Thread 10 is Killed!
Thread 12 is Killed!
Thread 8 is Killed!
Thread 13 is Killed!
Thread 7 is Killed!

总结

线程池作为非常关键的自定义组件,理应熟练掌握。而构建一个线程池实际不难,关键就是要掌握整体逻辑。

课程学习连接:https://xxetb.xet.tech/s/4czPSo

Github链接:Github

如果对你有帮助 请点个赞赞吧喵 谢谢喵 有错误欢迎指出

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值