线程池原理
我们使用线程的时候就去创建一个线程,这样实现起来非常简便,但是就会有一个问题:如果并发的线程数量很多,并且每个线程都是执行一个时间很短的任务就结束了,这样频繁创建线程就会大大降低系统的效率,因为频繁创建线程和销毁线程需要时间。
那么有没有一种办法使得线程可以复用,就是执行完一个任务,并不被销毁,而是可以继续执行其他的任务呢?
线程池是一种多线程处理形式,处理过程中将任务添加到队列,然后在创建 线程后自动启动这些任务。线程池线程都是后台线程。每个线程都使用默认的堆栈大小,以默认的优先级运行,并处于多线程单元中。如果某个线程在托管代码中空闲(如正在等待某个事件), 则线程池将插入另一个辅助线程来使所有处理器保持繁忙。如果所有线程池线程都始终保持繁忙,但队列中包含挂起的工作,则线程池将在一段时间后创建另一个辅助线程但线程的数目永远不会超过最大值。超过最大值的线程可以排队,但他们要等到其他线程完成后才启动。
在各个编程语言的语种中都有线程池的概念,并且很多语言中直接提供了线程池,作为程序猿直接使用就可以了,下面给大家介绍一下线程池的实现原理:
线程池的组成主要分为 3 个部分,这三部分配合工作就可以得到一个完整的线程池:
任务队列,存储需要处理的任务,由工作的线程来处理这些任务
通过线程池提供的 API 函数,将一个待处理的任务添加到任务队列,或者从任务队列中删除已处理的任务会被从任务队列中删除线程池的使用者,也就是调用线程池函数往任务队列中添加任务的线程就是生产者线程
工作的线程(任务队列任务的消费者) ,N个
线程池中维护了一定数量的工作线程,他们的作用是是不停的读任务队列,从里边取出任务并处理工作的线程相当于是任务队列的消费者角色,
如果任务队列为空,工作的线程将会被阻塞 (使用条件变量 / 信号量阻塞)
如果阻塞之后有了新的任务,由生产者将阻塞解除,工作线程开始工作
管理者线程(不处理任务队列中的任务),1个
它的任务是周期性的对任务队列中的任务数量以及处于忙状态的工作线程个数进行检测当任务过多的时候,可以适当的创建一些新的工作线程,当任务过少的时候,可以适当的销毁一些工作的线程。
以下是代码部分,一共有五个文件,分别是task.h、task.cpp、threadpool.h、threadpool.cpp、main.cpp
//task.h
#pragma once
#include<pthread.h>
#include<queue>
using namespace std;
using callback=void(*)(void*);
struct Task
{
Task(){
function=nullptr;
arg=nullptr;
}
Task(callback function,void* arg){
this->function=function;
this->arg=arg;
}
callback function;
void* arg;
};
class TaskQueue{
private:
pthread_mutex_t m_mutex;//the mutex
std::queue<Task> m_queue;//task queue
public:
TaskQueue();
~TaskQueue();
//add a task
void addTask(Task& task);
void addTask(callback func,void* arg);
//fetch a task
Task takeTask();
//fetch the number of task queue
inline int taskNumber(){
return m_queue.size();
}
};
//task.cpp
#include "task.h"
TaskQueue::TaskQueue(){
pthread_mutex_init(&m_mutex,NULL);
}
TaskQueue::~TaskQueue(){
pthread_mutex_destroy(&m_mutex);
}
void TaskQueue::addTask(Task& task){
pthread_mutex_lock(&m_mutex);
m_queue.push(task);
pthread_mutex_unlock(&m_mutex);
}
void TaskQueue::addTask(callback func,void* arg){
pthread_mutex_lock(&m_mutex);
Task task;
task.function=func;
task.arg=arg;
m_queue.push(task);
pthread_mutex_lock(&m_mutex);
}
Task TaskQueue::takeTask(){
Task task;
pthread_mutex_lock(&m_mutex);
if(!m_queue.empty()){
task=m_queue.front();
m_queue.pop();
}
pthread_mutex_unlock(&m_mutex);
return task;
}
//threadpool.h
#include"task.h"
class ThreadPool{
private:
int m_minNum;
int m_maxNum;
int m_busyNum;
int m_aliveNum;
int m_exitNum;
bool m_shutdown=false;
pthread_mutex_t m_lock;
pthread_cond_t m_full;
pthread_t* m_workID;
pthread_t m_managerID;
TaskQueue *m_taskQ;
private:
static void* worker(void* arg);
static void* manager(void* arg);
void threadExit();
public:
ThreadPool(int min,int max);
~ThreadPool();
void addTask(Task task);
int getBusyNumber();
int getAliveNumber();
};
//threadpool.cpp
#include<iostream>
#include<string.h>
#include<pthread.h>
#include<unistd.h>
using namespace std;
#include"threadpool.h"
ThreadPool::ThreadPool(int minNum,int maxNum){
m_taskQ=new TaskQueue;
do{
m_minNum=minNum;
m_maxNum=maxNum;
m_busyNum=0;
m_aliveNum=minNum;
m_exitNum=0;
//根据线程的最大值来分配线程号的内存
m_workID=new pthread_t[maxNum];
if(m_workID==nullptr){
cout<<"malloc thread_t[] fail....."<<endl;
break;
}
//初始化线程号数组
memset(m_workID,0,sizeof(pthread_t)*maxNum);
//初始化互斥锁、条件变量
if(pthread_mutex_init(&m_lock,NULL)!=0||
pthread_cond_init(&m_full,NULL)!=0){
cout<<"init mutex or condition fail"<<endl;
break;
}
///create thread/
/*根据最小线程数创建工作线程,调用pthread_create函数时,创建一个新线程,
m_workID数组返回新创建的线程号,然后执行回调函数woker,参数是这个初始化ThreadPool对象本身,
用this指针传递,相当于执行函数void* woker(void* arg),arg=this.*/
for(int i=0;i<minNum;i++){
pthread_create(&m_workID[i],NULL,worker,this);
cout<<"create work thread ID:"<<to_string(m_workID[i])<<endl;
sleep(1);
}
//创建一个工作线程
pthread_create(&m_managerID,NULL,manager,this);
cout<<"create manager thread ID:"<<to_string(m_managerID)<<endl;
}while(0);
};
ThreadPool::~ThreadPool(){
//销毁管理者线程
pthread_join(m_managerID,NULL);
//唤醒所有存活的消费者线程
for(int i=0;i<m_aliveNum;i++){
pthread_cond_signal(&m_full);
}
if(m_taskQ) delete m_taskQ;
if(m_workID) delete[] m_workID;
pthread_mutex_destroy( &m_lock);
pthread_cond_destroy(&m_full);
};
void ThreadPool::addTask(Task task){
if(m_shutdown==1){
return ;
}
// 添加任务,不需要加锁,任务队列中有锁
m_taskQ->addTask(task);
// 唤醒工作的线程
pthread_cond_signal(&m_full);
return ;
};
int ThreadPool::getBusyNumber(){
if(m_shutdown==1){
return 0;
}
int busynumber;
pthread_mutex_lock(&m_lock);
busynumber=m_busyNum;
pthread_mutex_unlock(&m_lock);
return busynumber;
}
int ThreadPool::getAliveNumber(){
if(m_shutdown==1){
return 0;
}
int Alivenumber;
pthread_mutex_lock(&m_lock);
Alivenumber=m_aliveNum;
pthread_mutex_unlock(&m_lock);
return Alivenumber;
}
/*调用pthread_create函数后,多个线程跳转执行work函数代码,
多个线程竞争共享资源(m_taskQ),刚开始任务队列为空,
调用pthread_cond_wait对线程进行阻塞,
等到生产者线程调用pthread_cond_signal唤醒阻塞线程上的某个线程,
工作线程就可以从任务队列中取一个线程。*/
void* ThreadPool::worker(void* arg){
ThreadPool* pool=static_cast<ThreadPool*>(arg);
// 一直不停的工作
while(1){
pthread_mutex_lock(&pool->m_lock);
// 判断任务队列是否为空, 如果为空工作线程阻塞
while(pool->m_taskQ->taskNumber()==0&&!pool->m_shutdown){
cout<<"thread:"<<to_string(pthread_self())<<" waiting..."<<endl;
// 阻塞线程
pthread_cond_wait(&pool->m_full,&pool->m_lock);
// 解除阻塞之后, 判断是否要销毁线程
if(pool->m_exitNum>0){
pool->m_exitNum--;
if(pool->m_aliveNum>pool->m_minNum){
pool->m_aliveNum--;
pthread_mutex_unlock(&pool->m_lock);
pool->threadExit();
}
}
}
if(pool->m_shutdown){
pthread_mutex_unlock(&pool->m_lock);
pool->threadExit();
}
// 从任务队列中取出一个任务
Task task=pool->m_taskQ->takeTask();
pool->m_busyNum++;
pthread_mutex_unlock(&pool->m_lock);
cout<<"thread:"<<to_string(pthread_self())<<" start working....."<<endl;
task.function(task.arg);
delete task.arg;
task.arg=nullptr;
// 任务处理结束
cout<<"thread:"<<to_string(pthread_self())<<" end working......"<<endl;
pthread_mutex_lock(&pool->m_lock);
pool->m_busyNum--;
pthread_mutex_unlock(&pool->m_lock);
}
return nullptr;
}
// 管理者线程任务函数
void* ThreadPool::manager(void* arg){
cout<<"hi i am turn in manager thread............................................................."<<endl;
ThreadPool* pool=static_cast<ThreadPool*>(arg);
// 如果线程池没有关闭, 就一直检测
while(!pool->m_shutdown){
// 每隔5s检测一次
sleep(5);
// 取出线程池中的任务数和线程数量
// 取出工作的线程池数量
pthread_mutex_lock(&pool->m_lock);
int queueSize=pool->m_taskQ->taskNumber();
int aliveNum=pool->m_aliveNum;
int busyNum=pool->m_busyNum;
pthread_mutex_unlock(&pool->m_lock);
//创建线程
const int NUMBER=2;
// 当前任务个数>存活的线程数 && 存活的线程数<最大线程个数
if(queueSize>aliveNum&&aliveNum<pool->m_maxNum){
pthread_mutex_lock(&pool->m_lock);
//循环遍历线程ID数组,找出一个空位(即线程ID=0),创建的新线程号用这个空位传出,同时保证每次只创建NUMBER个线程
for(int i=0,num=0;i<pool->m_maxNum&&num<NUMBER&&pool->m_aliveNum<pool->m_maxNum;i++){
if(pool->m_workID[i]==0){
pool->m_aliveNum++;
num++;
pthread_create(&pool->m_workID[i],NULL,worker,pool);
}
}
pthread_mutex_unlock(&pool->m_lock);
}
// 销毁多余的线程
// 忙线程*2 < 存活的线程数目 && 存活的线程数 > 最小线程数量
if(busyNum*2<aliveNum&&aliveNum>pool->m_aliveNum){
pthread_mutex_lock(&pool->m_lock);
pool->m_exitNum=NUMBER;
pthread_mutex_unlock(&pool->m_lock);
for(int i=0;i<NUMBER;i++){
pthread_cond_signal(&pool->m_full);
}
}
}
return nullptr;
}
//thread exit
void ThreadPool::threadExit(){
pthread_t tid=pthread_self();
for(int i=0;i<m_maxNum;i++){
if(m_workID[i]==tid){
cout<<"threadExit function thread"<<to_string(pthread_self())<<" exit........................................"<<endl;
m_workID[i]=0;
break;
}
}
pthread_exit(NULL);
}
//main.cpp
#include"task.h"
#include"threadpool.h"
#include<iostream>
#include<unistd.h>
using namespace std;
void taskFunc(void* arg){//任务函数
int num=*(int*)arg;
cout<<"thread ID:"<<pthread_self()<<" number="<<num<<endl;
sleep(1);
}
int main(){
ThreadPool* pool=new ThreadPool(10,20);
for(int i=0;i<100;++i){//循环的向任务队里加入100个任务,等待消费者线程来消费
int* num=(int*)malloc(sizeof(int));
*num=i+100;
Task task;
task.function=taskFunc;
task.arg=num;
pool->addTask(task);//向任务队列里加入任务,模拟生产者,addTask函数调用thread_cond_signal函数唤醒一个工作线程
cout<<"忙线程数:"<< pool->getBusyNumber()<<" ";
cout<<"活线程数"<<pool->getAliveNumber()<<endl;
//sleep(1);
}
sleep(3000);
}