线程池

本文详细介绍了线程池的原理与实现,包括如何降低资源消耗、提升系统响应速度和提高线程的可管理性。代码示例展示了线程池的创建、销毁、任务添加和线程管理,以及如何通过管理者线程动态调整线程数量以适应任务负载。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

线程池

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;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值