项目描述
线程池是一种多线程处理形式,首先将任务添加到任务队列,然后在创建线程后自动启动任务。每个线 程都是默认的大小和优先级。如果线程某个线程空闲(如有等待事件),那么线程池将会插入其他辅助线 程使服务器保持繁忙。线程池中的线程不会超过最大数,如果线程池中的线程都处于一个忙状态,则新 来的线程则需要排队等候。
设计思路
-
创建任务队列,存储待处理的任务
将待处理的任务添加到任务队列,将已经处理的任务从队列中移除
-
实现工作中的线程,读任务队列并处理
打工人线程的任务就是不停的读任务队列,从队列中取出任务并处理,如果任务队列为空,工作的线程将会被阻塞,等到有了新的任务才能解除阻塞。
-
实现管理者线程,对线程池的状态进行检测和维护
管理者线程的任务就是一直检查任务队列中的任务数量,任务多的时候,就添加部分打工人线程,任务少的时候就减少一部分。
主要的功能模块
数据结构(任务队列类和线程池类)
#pragma once
#include <queue>
#include <pthread.h>
using callback = void (*) (void* arg);//函数指针
//任务结构体
struct Task
{
Task()
{
function = nullptr;
arg = nullptr;
}
Task(callback f, void* arg)
{
this->arg = arg;
function = f;
}
callback function;
void* arg;//普通指针
};
class TaskQueue
{
public:
TaskQueue();
~TaskQueue();
//添加任务
void addTask(Task task);
void addTask(callback f, void* arg);
//取出一个任务
Task takeTask();
//获取当前任务个数(内联函数提高效率)
inline size_t taskNumber()
{
return m_taskQ.size();
}
private:
pthread_mutex_t m_mutex;
std::queue<Task> m_taskQ;
};
#include "TaskQueue.h"
//#include "TaskQueue.cpp"
class ThreadPool
{
public:
//创建线程池并初始化
ThreadPool(int min, int max);
//销毁线程池
~ThreadPool();
//给线程池添加任务
void addTask(Task task);
//获取线程池中工作的线程个数
int getBusyNum();
//获取线程池中活着的线程个数
int getAliveNum();
private:
static void* worker(void* arg);//工作的线程(消费者线程)任务函数
static void* manager(void* arg);//管理者线程任务函数
void threadExit();//单个线程退出
private:
//任务队列
TaskQueue* taskQ;
pthread_t managerID; //管理者线程ID
pthread_t* threadIDs; //工作的线程ID
int minNum; //最小线程数
int maxNum; //最大线程数
int busyNum; //忙的线程个数
int liveNum; //存活的线程个数
int exitNum; //要销毁的线程个数
pthread_mutex_t mutexPool;//锁整个线程池
pthread_cond_t notEmpty;//任务队列是否空了
static const int NUMBER = 2;//依次添加两个线程
bool shutdown; //是否销毁线程池,销毁为1
};
-
创建线程池并初始化
创建任务队列,一个管理者线程和若干打工人线程并初始化id为0.
ThreadPool::ThreadPool(int min, int max)
{
//实例化任务队列
taskQ = new TaskQueue;
if (taskQ == nullptr)
{
printf("new taskQ fail...\n");
return;
}
do {
threadIDs = new pthread_t[max]; //new一个工作者线程id数组
if (threadIDs == nullptr)
{
printf("new threadpool fail...\n");
break;
}
memset(threadIDs, 0, sizeof(pthread_t) * max);
//线程id初始化为0
minNum = min;
maxNum = max;
busyNum = 0;
liveNum = min; //和最小个数相等
exitNum = 0;
if(pthread_mutex_init(&mutexPool, NULL) != 0 || pthread_cond_init(¬Empty, NULL) != 0)
{
std::cout<<"mutex or condition init fail..."<<endl;
break;
}
shutdown = false;
//创建一个管理者线程和若干工作者线程
pthread_create(&managerID, NULL, manager, this);
for (int i = 0; i < min; i++)
{
pthread_create(&threadIDs[i], NULL, worker, this);
}
return;
} while (0);
//释放资源
if (threadIDs) delete[]threadIDs;
if (taskQ) delete taskQ;
}
-
管理者线程
根据队列中的线程数判断是否添加/减少线程
void* ThreadPool::manager(void* arg)
{
ThreadPool* pool = static_cast<ThreadPool*>(arg);
while (!pool->shutdown)//shutdown等于0的时候开始干活
{
//每隔3秒检测一次,检测线程池是否被关闭
sleep(3);
//取出线程池中任务数量和当前线程数量
pthread_mutex_lock(&pool->mutexPool);
size_t queueSize = pool->taskQ->taskNumber();
int liveNum = pool->liveNum; // 取出存活线程的数量
int busyNum = pool->busyNum;
pthread_mutex_unlock(&pool->mutexPool);
//什么时候添加线程(干活人少了)
//任务的个数>存活的线程个数 && 存活的线程数<最大的线程数,否则不能继续添加新线程
if (queueSize > liveNum && liveNum < pool->maxNum)
{
pthread_mutex_lock(&pool->mutexPool);
int counter = 0;
for (int i = 0; i < pool->maxNum && counter < NUMBER && pool->liveNum < pool->maxNum; i++)
{
if (pool->threadIDs[i] == 0)//寻找没有存储线程id
{
pthread_create(&pool->threadIDs[i], NULL, worker, pool);//把线程id放在数组里
counter++;
pool->liveNum++;
}
}
pthread_mutex_unlock(&pool->mutexPool);
}
//销毁线程
//忙的线程*2<存活的线程数 && 存活的线程数>最小线程数 存活的线程很多并且干活的线程很少
if (busyNum * 2 < liveNum && liveNum > pool->minNum)
{
pthread_mutex_lock(&pool->mutexPool);
pool->exitNum = NUMBER;
pthread_mutex_unlock(&pool->mutexPool);
//让工作线程自杀
for (int i = 0; i < NUMBER; i++)
{
pthread_cond_signal(&pool->notEmpty);
}
}
}
return nullptr;
}
3.打工人线程
void* ThreadPool::worker(void* arg)
{
//传进来的其实是threadpool的实例对象,需对arg进行类型转换成threadpool类型
ThreadPool* pool = static_cast<ThreadPool*>(arg);
while (true)//线程进入循环,从线程池中不停的取任务
{
pthread_mutex_lock(&pool->mutexPool);
//当前队列是否为空并且没有被关闭
while (pool->taskQ->taskNumber() == 0 && !pool->shutdown)
{
//阻塞工作线程,notempty记录了哪些线程被阻塞,mutexpool同时被解开
pthread_cond_wait(&pool->notEmpty, &pool->mutexPool);
//判断是否要销毁线程
if (pool->exitNum > 0) //工作的线程醒了,这里是管理者线程要做的会给exitnum初始化
{
pool->exitNum--;
if (pool->liveNum > pool->minNum)
{
pool->liveNum--;
pthread_mutex_unlock(&pool->mutexPool);
pool->threadExit();
}
}
}
//判断线程池是否被关闭
if (pool->shutdown)
{
//关闭了则打开互斥锁,退出当前的线程
pthread_mutex_unlock(&pool->mutexPool);
pool->threadExit();
}
//从任务队列中取任务
Task task = pool->taskQ->takeTask();
//移动头结点
//解锁
pool->busyNum++;
pthread_mutex_unlock(&pool->mutexPool);//解锁后其他线程就可以访问线程池里的数据
printf("thread %ld start working...\n", pthread_self());
task.function(task.arg);
delete task.arg;
task.arg = nullptr;
printf("thread %ld end workong...\n", pthread_self());
pthread_mutex_lock(&pool->mutexPool);
pool->busyNum--;
pthread_mutex_unlock(&pool->mutexPool);
}
return nullptr;
}