线程池
1>.**降低资源消耗。**通过复用已存在的线程,降低线程创建\关闭的次数来尽可能降低系统性能损耗;
2>.**提升系统响应速度。**通过复用线程,省去创建线程的过程,因此整体上提升了系统的响应速度;
3>.**提高线程的可管理性。**线程是稀缺资源,如果无限制的创建,不仅会消耗系统资源,还会降低系统的稳定性,因此,需要使用线程池来管理线程。
4>实现
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<string.h>
#include<pthread.h>
#include<signal.h>
#include<errno.h>
#define INIT_MIN_CREATE_THREAD_NUMBER 3
#define INIT_MAX_CREATE_THREAD_NUMBER 1000
#define INIT_MAX_QUEUE_SIZE 1000
#define true 1
#define false 0
#define DELAY_TIME 10
#define MIN_WAIT_TASK_NUM 10 //任务队列的实时任务数大于这个数则需要创建新的工作线程
#define DEFAULT_THREAD_VARY 10 //默认的每次创建和销毁线程数
typedef struct {
void* arg; //回调函数参数
void*(*function)(void*);//回调函数
}threadpool_task_t;
//线程池相关的信息,封装为结构体,好传输
typedef struct {
//定义两把锁,一个条件变量
pthread_mutex_t lock; //用于锁住本结构体,和条件变量一起使用
pthread_mutex_t thread_counter; //用于记录忙状态线程个数变量的锁(虽然有lock结构体全局锁,但是在只需用到部分变量时,就可加一个小锁,不妨碍工作线程工作)
pthread_cond_t queue_not_empty; //当任务队列满时,添加任务的线程阻塞,等待此条件变量
pthread_cond_t queue_not_full; //任务队列里不为空时,通知线程池中等待任务的线程
pthread_t admin_tid; // 存放管理线程tid
//统计各种线程数量
pthread_t * threads; //存放线程池中每个线程的tid 数组
int min_thread_num; //线程池最小线程数
int max_thread_num; //线程池最大线程数
int live_thread_num; //当前存活线程个数
int busy_thread_num; //忙状态线程个数
int wait_exit_thread_num; //要销毁的线程个数
//队列相关变量
threadpool_task_t *task_queue; //任务队列
int queue_front; //task_queue 队头下标
int queue_rear; // 队尾下标
int queue_realtime_size; // 队中实际任务数
int queue_max_size; // 队列可容纳任务数上限
int shutdown; //线程池使用状态标志位,true或false
}threadpool_t;
//使用的所有接口函数
void* threadpool_thread(void* arg); //pool线程池各个工作线程的回调函数
int threadpool_destroy(threadpool_t* pool); //杀死并回收所有活着的线程
int threadpool_free(threadpool_t* pool); //释放内存baocuo数,防止内存泄露
int thread_is_alive(pthread_t tid); //检查线程是否存在
void* admin_thread_thread(void* arg); //管理者线程
void* threadpool_add(threadpool_t *pool,void* (*function)(void* arg),void* arg); //向任务对列里加入任务
threadpool_t *threadpool_create(int min_thread_num,int max_thread_num ,int queue_max_size);//初始化pool
void * process(void * arg); //回调函数
//杀死并回收所有活着的线程
int threadpool_destroy(threadpool_t* pool)
{
int i;
if(pool==NULL)
return -1;
pool->shutdown=true;
//循环广播所有线程
for(i=0;i<pool->live_thread_num;i++)
pthread_cond_broadcast(&pool->queue_not_empty);
for(i=0;i<pool->live_thread_num;i++)
pthread_join(pool->threads[i],NULL);
threadpool_free(pool);
return NULL;
}
//释放内存baocuo数,防止内存泄露
int threadpool_free(threadpool_t* pool)
{
if(pool == NULL)
return -1;
//注意:free只能释放(threadpool_t*)malloc(sizeof(threadpool_t))此处开辟的空间,不能释放结构体里面的指针所指向的地址
if(pool->threads)
free(pool->threads);
if(pool->task_queue)
free(pool->task_queue);
//释放各个锁
pthread_mutex_unlock(&(pool->lock)); //先释放,保证成功销毁
pthread_mutex_destroy(&(pool->lock));
pthread_mutex_unlock(&(pool->thread_counter));
pthread_mutex_destroy(&(pool->thread_counter));
pthread_cond_destroy(&(pool->queue_not_empty));
pthread_cond_destroy(&(pool->queue_not_full));
//最后释放结构体
free(pool);
pool = NULL; //释放后赋空,防止循环释放报错
return 0;
}
//检查线程是否存在
int thread_is_alive(pthread_t tid)
{
int kill_rc=pthread_kill(tid,0);//第二个参数不是0,一定要实现线程的信号处理函数,否则,就会影响整个进程。如果是0,这是一个保留信号,
//一个作用是用来判断线程是不是还活着。pthread_kill的返回值:成功:0;线程不存在:ESRCH;信号不合法:EINVAL
if(kill_rc==ESRCH)
{
return true;
}
return false;
}
//管理者线程回调函数
void* admin_thread_thread(void* arg)
{
int i;
threadpool_t* pool=(threadpool_t* )arg;
while(pool->shutdown==false)
{
sleep(DELAY_TIME); //减少资源消耗,延迟一段时间
printf("******************************************************%d\n",pool->live_thread_num);
printf("10s is finish,start test thread pool\n");
pthread_mutex_lock(&(pool->lock));
int queue_size = pool->queue_realtime_size; //队列现有任务数
int live_thread_num = pool->live_thread_num; //存活线程数
pthread_mutex_unlock(&(pool->lock));
pthread_mutex_lock(&(pool->thread_counter));
int busy_thr_num = pool->busy_thread_num; //工作线程数
printf("busy_thr_num is %d\n",busy_thr_num);
pthread_mutex_unlock(&(pool->thread_counter));
//创建新线程,算法: 任务书大于最小线程池个数,且存活的线程数少于最大线程个数时。如30>=10 && 40 < 100
if(queue_size >= MIN_WAIT_TASK_NUM && live_thread_num < pool->max_thread_num)
{
printf("create nuw thread\n");
pthread_mutex_lock(&(pool->lock));
int add = 0;
//一次增加DEFAULT_THREAD个线程
for(i = 0;i< pool->max_thread_num && add < DEFAULT_THREAD_VARY &&pool->live_thread_num < pool->max_thread_num ; i++)
{
if(pool->threads[i] == 0 || thread_is_alive(pool->threads[i]))
{
pthread_create(&(pool->threads[i]),NULL,threadpool_thread,(void *)pool);
add ++;
pool->live_thread_num++;
}
}
pthread_mutex_unlock(&(pool->lock));
}
//销毁多余的空闲线程,忙线程×2 小于存货的线程数,且存活的线程数大于最小线程数时
if((busy_thr_num * 2) < live_thread_num && live_thread_num > pool->min_thread_num )
{
printf("delete pthread\n");
//一次销毁DEFAULT_THREAD个线程。随机10个即可
pthread_mutex_lock(&(pool->lock));
pool->wait_exit_thread_num = DEFAULT_THREAD_VARY; //要销毁的线程数设为10
pthread_mutex_unlock(&(pool->lock));
for(i = 0;i< DEFAULT_THREAD_VARY; i++)
{
//通知处在空闲状态的线程,他们会自行终止
pthread_cond_signal(&(pool->queue_not_empty));
}
}
}
return NULL;
}
//向任务对列里加入任务
void* threadpool_add(threadpool_t *pool,void* (*function)(void* arg),void* arg)//函数指针作为形参
{
pthread_mutex_lock(&(pool->lock));
while((pool->queue_realtime_size==pool->queue_max_size)&&(pool->shutdown==false))
{
pthread_cond_wait(&(pool->queue_not_full),&(pool->lock));//如果满,则阻塞,等待工作线程拿出任务后唤醒
}
if(pool->shutdown==true)
{
pthread_mutex_unlock(&pool->lock);
}
printf("%d\n",pool->queue_rear);
printf("%d\n",pool->task_queue[pool->queue_rear].arg);
if (pool->task_queue[pool->queue_rear].arg != NULL)
{
pool->task_queue[pool->queue_rear].arg = NULL; //malloc出来的地址只能free一次,第二次循环进入将free两次,防止多次free,使其等与NULL(可以多次free)
free(pool->task_queue[pool->queue_rear].arg);
pool->task_queue[pool->queue_rear].arg = NULL;
}
//向队尾加入任务
pool->task_queue[pool->queue_rear].arg=arg;
pool->task_queue[pool->queue_rear].function=function;
pool->queue_rear=(++pool->queue_rear)%pool->queue_max_size;
//更新相关变量
pool->queue_realtime_size++; //更新队列现有任务数
pthread_cond_signal(&pool->queue_not_empty);//队列不为空,唤醒阻塞.
pthread_mutex_unlock(&pool->lock);
return 0;
}
//pool线程池各个工作线程的回调函数
void* threadpool_thread(void* arg)
{
threadpool_t* pool=(threadpool_t*)arg;
threadpool_task_t task;
int ret;
while(true)
{
//多个工作要对threadpool_t全局变量访问,先加锁
pthread_mutex_lock(&(pool->lock));
//使用条件变量-判断队列是否有任务(没有阻塞,等待唤醒信号)
while((pool->queue_realtime_size==0)&&(pool->shutdown==false))
{
printf("thread %d is waiting\n",(unsigned int)pthread_self());
pthread_cond_wait(&(pool->queue_not_empty),&(pool->lock));//阻塞等待唤醒信号
//如果要结束的线程个数大于0,结束线程
if(pool->wait_exit_thread_num > 0) //要销毁的线程个数大于0
{
pool->wait_exit_thread_num --;
//如果线程池里线程个数大于最小值时可以结束当前线程
if(pool->live_thread_num > pool->min_thread_num )
{
printf("thread 0x%x is exiting\n",(unsigned int )pthread_self());
pool->live_thread_num --;
pthread_mutex_unlock(&(pool->lock));
pthread_exit(NULL);
}
}
}
//当需要关闭线程池所有线程时进入
if(pool->shutdown==true)
{
pthread_mutex_unlock(&pool->lock);
printf("thread %d is exiting\n",(unsigned int)pthread_self());
pthread_exit(NULL); //被骗自杀
}
//从任务队列拿去任务(出队过程)
task.arg=pool->task_queue[pool->queue_front].arg;
task.function=pool->task_queue[pool->queue_front].function;
//更新线程池数据
pool->queue_front=(++pool->queue_front)%pool->queue_max_size;//更新头指针
pool->queue_realtime_size--; //更新队列任务数
pthread_cond_broadcast(&pool->queue_not_full);
//实现对大的结构体加锁密度的最小化.后面再次访问其他参数加小锁.把结构体的大锁让出来.
pthread_mutex_unlock(&pool->lock);
pthread_mutex_lock(&pool->thread_counter);//加小锁,访问变量
pool->busy_thread_num++;
pthread_mutex_unlock(&pool->thread_counter);//解锁
ret=(task.function)(task.arg); //调用回调函数(process(arg));
if(ret)
{
pthread_mutex_lock(&pool->thread_counter);
pool->busy_thread_num--;
pthread_mutex_unlock(&pool->thread_counter);
printf("thread %d end working\n",(unsigned int )pthread_self());
}
}
pthread_exit(NULL);
}
//主线程初始化:1.初始化threadpool_t(线程池相关变量);2.分配相应的空间;3.创建线程
threadpool_t *threadpool_create(int min_thread_num,int max_thread_num ,int queue_max_size)
{
int i,ret0,ret1;
threadpool_t* pool=NULL;
//使用do---while(0)加break语句实现,只要有一个环节出错则放弃全部
do
{
if((pool=(threadpool_t*)malloc(sizeof(threadpool_t)))==NULL)
{
printf("malloc thread_pool1 space fail\n");
break;
}
pool->min_thread_num=min_thread_num; //线程池最小线程数
pool->max_thread_num=max_thread_num; //线程池最大线程数
pool->live_thread_num=min_thread_num;
pool->busy_thread_num=0;
pool->wait_exit_thread_num=0;
pool->queue_front=0;
pool->queue_rear=0; //此时queue为空,队首尾均为空
pool->queue_realtime_size=0;
pool->queue_max_size=queue_max_size; //指定队列的大小
pool->shutdown = false; //不关闭线程池
//存储线程信息的数组开辟空间(一次开足),并清零
pool->threads=(pthread_t*)malloc(sizeof(pthread_t)*max_thread_num);
if(pool->threads==NULL)
{
printf("malloc threads[max_thread_num] space fail\n");
break;
}
memset(pool->threads,0,sizeof(pthread_t)*max_thread_num);
//队列开辟空间
pool->task_queue=(threadpool_task_t*)malloc(sizeof(threadpool_task_t)*queue_max_size);
if(pool->task_queue==NULL)
{
printf("malloc task_queue[max_queue_num] space fail\n");
break;
}
memset(pool->task_queue,0,sizeof(threadpool_task_t)*queue_max_size);
//初始化互斥锁
if(pthread_mutex_init(&(pool->lock),NULL)!=0||
pthread_mutex_init(&(pool->thread_counter),NULL)!=0||
pthread_mutex_init(&(pool->queue_not_empty),NULL)!=0||
pthread_mutex_init(&(pool->queue_not_full),NULL)!=0)
{
perror("init lock errno:");
break;
}
//创建INIT_MIN_CREATE_THREAD_NUMBER个工作线程和管理者线程
for(i=0;i<INIT_MIN_CREATE_THREAD_NUMBER;i++)
{
ret0=pthread_create(&pool->threads[i], NULL,threadpool_thread,(void*)pool);
if(ret0!=0)
{
fprintf(stderr,"pthread_create error:%s\n",strerror(ret0)); //char *strerror(int errnum);//该函数只需传入错误号,然后就会输出对应的错误描述(进程编程就会直接返回错误描述);int fprintf(FILE *stream, const char *format, ...);
break;
}
printf("start thread %d...\n",(unsigned int )pool->threads[i]);//打印线程id
}
ret1=pthread_create(&pool->admin_tid, NULL,admin_thread_thread,(void*)pool);
if(ret1!=0)
{
fprintf(stderr,"admin_pthread_create error:%s\n",strerror(ret1)); //char *strerror(int errnum);//该函数只需传入错误号,然后就会输出对应的错误描述(进程编程就会直接返回错误描述);int fprintf(FILE *stream, const char *format, ...);
break;
}
printf("admin_thread start working......\n");
return pool;
} while (0);
printf("%d\n",pool->min_thread_num);
threadpool_free(pool); //do---while(0)任意一处调用失败时,均释放pool存储空间
return NULL;
}
//线程池中的线程,模拟处理任务
void * process(void * arg)
{
printf("thread %d working on task %d--------------\n",(unsigned int)pthread_self(),*(int *)arg);
return 1;
}
int main(int argc,char* argv[])
{
//创建线程池,指定初始化时创建的线程数,队列大小(也是最大的线程数).
threadpool_t * thread_pool1=threadpool_create(INIT_MIN_CREATE_THREAD_NUMBER,
INIT_MAX_CREATE_THREAD_NUMBER,INIT_MAX_QUEUE_SIZE );
printf("thread_pool1 init success.\n");
int num[1000],i;
for(i = 0;i < 1000;i++)
{
num[i] = i;
//对外的接口
threadpool_add(thread_pool1,process,(void *)&num[i]); //向任务队列中添加任务
//sleep(1);
}
sleep(100); //等子线程完成任务
printf("clean up thread pool \n");
sleep(1);
threadpool_destroy(thread_pool1);
return 0;
}