如果多次使用线程,那么就需要多次的创建并撤销线程。但是创建/撤销的过程会消耗资源。线程池是一种数据结构,其中维护着多个线程,这避免了在处理短时间任务时,创建与销毁线程的代价。即在程序开始运行前预先创建一定数量的线程放入空闲队列中,这些线程都是处于阻塞状态,基本不消耗CPU,只占用较小的内存空间,程序在运行时,只需要从线程池中拿来用就可以了,大大提高了程序运行效率。
#include
#include //锁类
#include
using namespace std;
using callback=void (*) (void* args);
typedef class myTask{ //任务
public:
callback function; // 任务函数 void function(void* arg)类型的指针
void* args; // 任务函数的参数
myTask(){ //无参构造函数
function=nullptr;
args=nullptr;
}
myTask(callback function, void *args){ //有参构造函数
this->function = function;
this->args = args;
}
~myTask(){} //析构函数
}task;
class myTaskQueue{ // 任务队列
private:
queue<task>q; //任务队列
mutex m; //任务队列的互斥锁
public:
myTaskQueue(); //无参构造
void add(task t); //添加任务
task get(); //取出任务
bool is_empty(); //判断是否为空
int get_length(); //返回队列中任务数量
};
#include "myTaskQueue.hpp"
using namespace std;
myTaskQueue::myTaskQueue(){
//空构造
}
void myTaskQueue::add(task t){
lock_guard<mutex> lg(m); //上锁
q.push(t);
}
task myTaskQueue::get(){
lock_guard<mutex> lg(m); //上锁
task ret = q.front();
q.pop();
return ret;
}
bool myTaskQueue::is_empty(){
lock_guard<mutex> lg(m); //上锁
return q.empty();
}
int myTaskQueue::get_length(){
lock_guard<mutex> lg(m); //上锁
return (int)q.size();
}
#include
#include //线程类
#include //条件变量锁类
#include //互斥锁类
#include "myTaskQueue.hpp" //任务队列类
using namespace std;
typedef class myThreadPool{ // 线程池类
private:
//任务队列相关
myTaskQueue taskQ;
//工作线程相关
thread** worker; //工作线程的二级指针(thread*类型的数组)
int minNum; //最小线程数
int maxNum; //最大线程数
int liveNum; //存活的线程数
int busyNum; //忙碌的线程数
//管理线程相关
thread *manager; //管理线程
int deleteNum; //要删除的线程数
int shutdown; //判断线程池是否关闭
//锁相关
mutex m; //临界资源互斥锁
condition_variable_any condEmpty; //条件锁,判断任务队列是否为空
static void* work(void* args); //工作线程函数
static void* manage(void* args); //管理线程函数
public:
myThreadPool(int minNum=3, int maxNum=10); //构造函数
~myThreadPool(); //析构函数
void add_task(task t); //添加任务
int get_live_thread_num(); //查看存活的线程数量
int get_busy_thread_num(); //查看忙碌的线程数量
int get_task_num(); //查看队列中任务的数量
void thread_exit(); //线程退出函数
}threadPool;
#include "myThreadPool.hpp"
#include
#include
using namespace std;
using callback=void (*)(void* args);
myThreadPool::myThreadPool(int minNum, int maxNum){
//初始化工作线程
this->worker = new thread* [maxNum]; //初始化10个thread*类型
for(int i=0;i<maxNum;++i)worker[i]=nullptr; //赋值为nullptr
for(int i=0;i<minNum;++i){ //初始化minNum个线程
worker[i] = new thread(work, this); //这里需要传递this,因为work和manage都是静态成员函数,不能直接访问类的普通成员
}
this->minNum = minNum;
this->maxNum = maxNum;
this->liveNum = minNum;
this->busyNum = 0;
// 初始化管理线程
manager = new thread(manage, this); //这里需要传递this,因为work和manage都是静态成员函数,不能直接访问类的普通成员
deleteNum = 0;
shutdown = 0;
// 初始化锁
// 无需初始化
cout<<"线程池创建成功"<<endl;
}
// 析构函数
myThreadPool::~myThreadPool(){
shutdown=1;
this->manager->join(); //等待管理线程的终止
// 唤醒所有的worker线程
for(int i=0;i<this->maxNum;++i){
this->condEmpty.notify_all();
}
//阻塞等待所有worker线程退出
for(int i=0;i<this->maxNum;++i){
if(worker[i]!=nullptr)worker[i]->join();
}
if(this->worker)delete worker;
if(this->manager)delete manager;
cout<<"线程池删除成功"<<endl;
}
//每次从任务队列中取出一个任务并执行
void* myThreadPool::work(void* args){ //这里arg是一个pool的this指针
//拿到对象的指针
myThreadPool* pool = (myThreadPool*)args;
// 循环执行
while(1){
unique_lock<mutex> ulock(pool->m); //静态成员函数可以访问类的私有成员,访问临界资源,上互斥锁,lock_guard会自动释放锁
while(!pool->shutdown && pool->taskQ.get_length()==0){ //如果没有任务,则需要阻塞等待。注意这里不能直接pool->get_task_num(),因为该函数内部也需要互斥锁pool->m,会出现死锁
pool->condEmpty.wait(ulock); //等待有任务
if(pool->deleteNum>0){
pool->deleteNum--;
pool->liveNum--;
pool->thread_exit();
return nullptr;
}
}
if(pool->shutdown){ //如果要关闭线程池,则销毁当前线程
pool->thread_exit();
return nullptr; //需要返回nullptr让线程走完之后自动销毁
}
task t = pool->taskQ.get(); // 拿到任务
pool->busyNum++; //忙碌的线程+1
ulock.unlock(); //解锁,上面有一些退出的位置不需要手动unlock,因为unique_lock会自动unlock
t.function(t.args); //执行任务
//任务执行完毕,则busyNum-1
pool->m.lock();
pool->busyNum--;
pool->m.unlock();
}
return nullptr;
}
void* myThreadPool::manage(void* args){
//拿到对象的指针
myThreadPool* pool = (myThreadPool*)args;
//循环执行
while(!pool->shutdown){
sleep(5); //每5秒检测一次
printf("manager开始工作\n");
// 访问临界资源,并得到存活的线程数liveNum和任务数taskNum,作为判断线程数量是否合适的依据
pool->m.lock();
int liveNum = pool->liveNum;
int taskNum = pool->taskQ.get_length(); //注意这里不能直接pool->get_task_num(),因为该函数内部也需要互斥锁pool->m,会出现死锁
pool->m.unlock();
//如果存活的线程太少,则增加线程,每次增加2个
if(taskNum > liveNum && liveNum < pool->maxNum){
unique_lock<mutex>ulock(pool->m); //上锁,if结束自动解锁
int num=0;
for(int i=0;i<pool->maxNum && pool->liveNum < pool->maxNum && num<2; ++i){
if(pool->worker[i]==nullptr){
pool->worker[i] = new thread(work, pool); //新建线程
num++;
pool->liveNum++;
}
}
cout<<"2 threads are added by manager\n";
}
//如果存活的线程太多,则减少线程数目,每次减少2个
if(taskNum*2 < liveNum && liveNum > pool->minNum){
unique_lock<mutex>ulock(pool->m); //上锁,if结束自动解锁
// 这里不能指定杀死某个线程,因为并不知道线程的状态(空闲/忙碌),要让空闲的线程自己自杀
if(pool->liveNum-2>pool->minNum)pool->deleteNum=2;
cout<<"2 threads are deleted by manager\n";
}
}
return nullptr;
}
void myThreadPool::add_task(task t){ //添加任务
unique_lock<mutex> ulock(this->m); //加锁,函数结束自动释放锁
this->taskQ.add(t); //向队列中添加任务
this->condEmpty.notify_all();//告知工作线程有任务了
}
int myThreadPool::get_live_thread_num(){ //返回存活的线程数木
unique_lock<mutex> ulock(this->m); //加锁,函数结束自动释放锁
return this->liveNum;
}
int myThreadPool::get_busy_thread_num(){ //返回忙碌的线程数目
unique_lock<mutex> ulock(this->m); //加锁,函数结束自动释放锁
return this->busyNum;
}
int myThreadPool::get_task_num(){ // 返回任务队列中任务的数目
unique_lock<mutex> ulock(this->m); //加锁,函数结束自动释放锁
return this->taskQ.get_length();
}
void myThreadPool::thread_exit(){
//找到当前的线程并删除
thread::id this_id = this_thread::get_id(); //得到当前线程的id
//找到该线程,并删除
for(int i=0;i<this->maxNum;++i){
if(this->worker[i]!=nullptr && this->worker[i]->get_id() == this_id){
// delete this->worker[i]; //这里不能直接delete,因为会直接导致当前线程终止在这里,不执行下面的赋值nullptr的操作,从而出现野指针
this->worker[i]=nullptr; //worker[i]赋值为nullptr,表示没有线程
this->liveNum--; //存活的线程数目-1
break;
}
}
}
#include
#include
#include
#include "myThreadPool.hpp"
using namespace std;
void test_task(void *args){
int num = *(int*)args;
printf("正在执行%d任务\n",num);
sleep(1);
}
int main(){
threadPool* pool = new threadPool(3, 10);
for(int i=0;i<100;++i){
int *num = new int(i);
task t(test_task, num);
// printf("123\n");
pool->add_task(t);
}
while(pool->get_live_thread_num()!=0 && pool->get_task_num()){
sleep(5);
printf("存活线程数为%d,忙碌线程数为%d, 任务数为%d\n", pool->get_live_thread_num(), pool->get_busy_thread_num(),pool->get_task_num());
}
sleep(10); //等待10s,测试manager的减少线程的操作
delete pool;
return 0;
}