• Linux线程池的一个简单实现


    在服务器开发中,当主线程/进程监听到读写任务时通常需要子线程/进程去处理任务。此时最容易想到的是,每当有一个新任务到来时,我们再创建一个新线程,处理完任务之后我们再把它销毁。这种办法当然能达到多线程服务器的目的,但是如果创建线程和销毁线程的时间比线程处理请求的时间长,而且请求很多的情况下,我们的CPU资源都浪费在了创建和销毁线程上了,所以这种方法的效率比较低,于是,我们可以将若干已经创建完成的线程放在一起统一管理,如果来了一个请求,我们从线程池中取出一个线程来处理,处理完了放回池内等待下一个任务,线程池的好处是避免了繁琐的创建和结束线程的时间,有效的利用了CPU资源。那么我们就把这种技术称为“线程池”。

    在展示代码之前我们先聊聊一个线程池应该是什么样子的?

    首先的有一个线程池类pthread_pool,这个类里面应该有什么:

    存储线程的数组threads,存储待处理任务的任务队列task_queue,线程池的互斥锁,任务队列的互斥锁和条件队列锁(因为其实任务队列就是一个经典的生产者消费者问题嘛,可以用经典的互斥+条件变量解决)

    当然还有线程池的状态:线程总数,忙线程数,线程池是否工作等

    那么又应该有哪些函数?

    容易想到的线程的创建和销毁,往任务队列添加新任务,工作线程的回调函数,获取线程池状态。

    看起来上诉线程池的线程数量是固定的,此时我们思考如果新增任务的数量远大于线程能处理的速度,那么此时线程池的工作效率肯定不高。

    于是我们想是不是可以动态改变线程的数量呢?当然可以,那么我们就需要一个根据现在忙线程数与总线程数的比例对当前总线程数做出动态调整(增加或者销毁)的函数,adjust_thread函数每隔一段时间检测现在线程池的忙碌情况对线程总数做出动态调整。

    那么我们又该在哪里执行这个函数呢?欸,我们得在线程池类中增加一个特殊的线程:管理线程。管理线程不是工作线程,它不处理新任务。它要定时检测现在线程池的忙碌情况对线程总数做出动态调整。

    到这里,线程池的基本知识就讲完了,详细实现看代码:

     1 #ifndef __THREADPOOL_H_
     2 #define __THREADPOOL_H_
     3 
     4 typedef struct threadpool_t threadpool_t;
     5 
     6 //创建新线程
     7 threadpool_t *threadpool_create(int min_thr_num, int max_thr_num, int queue_max_size);
     8 
     9 //往线程池队列里添加新任务
    10 int threadpool_add(threadpool_t *pool, void*(*function)(void *arg), void *arg);
    11 
    12 //销毁线程池
    13 int threadpool_destroy(threadpool_t *pool);
    14 
    15 //获得线程总线程数
    16 int threadpool_all_threadnum(threadpool_t *pool);
    17 
    18 //获得忙线程数
    19 int threadpool_busy_threadnum(threadpool_t *pool);
    20 
    21 #endif
    thread_pool.h
      1 #include <stdlib.h>
      2 #include <pthread.h>
      3 #include <unistd.h>
      4 #include <assert.h>
      5 #include <stdio.h>
      6 #include <string.h>
      7 #include <signal.h>
      8 #include <errno.h>
      9 #include "threadpool.h"
     10 
     11 #define DEFAULT_TIME 10                 /*管理线程时间间隔,10s检测一次*/
     12 #define MIN_WAIT_TASK_NUM 10            /*如果queue_size > MIN_WAIT_TASK_NUM 添加新的线程到线程池*/ 
     13 #define DEFAULT_THREAD_VARY 10          /*每次创建和销毁线程的个数*/
     14 #define true 1
     15 #define false 0
     16 
     17  /* 各工作线程任务结构体 */
     18 typedef struct {
     19     void *(*function)(void *);          /* 函数指针,回调函数 */
     20     void *arg;                          /* 上面函数的参数 */
     21 } threadpool_task_t;                   
     22 
     23 /* 描述线程池相关信息 */
     24 struct threadpool_t {
     25     pthread_mutex_t lock;               /* 用于锁住本结构体 */    
     26     pthread_mutex_t thread_counter;     /* 记录忙状态线程个数de琐 -- busy_thr_num */
     27     pthread_cond_t queue_not_full;      /* 当任务队列满时,添加任务的线程阻塞,等待此条件变量 */
     28     pthread_cond_t queue_not_empty;     /* 任务队列里不为空时,通知等待任务的线程 */
     29 
     30     pthread_t *threads;                 /* 存放线程池中每个线程的tid。数组 */
     31     pthread_t adjust_tid;               /* 存管理线程tid */
     32     threadpool_task_t *task_queue;      /* 任务队列 */
     33 
     34     int min_thr_num;                    /* 线程池最小线程数 */
     35     int max_thr_num;                    /* 线程池最大线程数 */
     36     int live_thr_num;                   /* 当前存活线程个数 */
     37     int busy_thr_num;                   /* 忙状态线程个数 */
     38     int wait_exit_thr_num;              /* 要销毁的线程个数 */
     39 
     40     int queue_front;                    /* task_queue队头下标 */
     41     int queue_rear;                     /* task_queue队尾下标 */
     42     int queue_size;                     /* task_queue队中实际任务数 */
     43     int queue_max_size;                 /* task_queue队列可容纳任务数上限 */
     44 
     45     int shutdown;                       /* 标志位,线程池使用状态,true或false */
     46 };
     47 
     48 
     49 void *threadpool_thread(void *threadpool);
     50 
     51 void *adjust_thread(void *threadpool);
     52 
     53 int is_thread_alive(pthread_t tid);
     54 
     55 int threadpool_free(threadpool_t *pool);
     56 
     57 threadpool_t *threadpool_create(int min_thr_num, int max_thr_num, int queue_max_size)
     58 {
     59     int i;
     60     threadpool_t *pool = NULL;
     61     do {
     62         if((pool = (threadpool_t *)malloc(sizeof(threadpool_t))) == NULL) {  
     63             printf("malloc threadpool fail");
     64             break;/*跳出do while*/
     65         }
     66 
     67         pool->min_thr_num = min_thr_num;
     68         pool->max_thr_num = max_thr_num;
     69         pool->busy_thr_num = 0;
     70         pool->live_thr_num = min_thr_num;               /* 活着的线程数 初值=最小线程数 */
     71         pool->queue_size = 0;                           /* 有0个产品 */
     72         pool->queue_max_size = queue_max_size;
     73         pool->queue_front = 0;
     74         pool->queue_rear = 0;
     75         pool->shutdown = false;                         /* 不关闭线程池 */
     76 
     77         /* 根据最大线程上限数, 给工作线程数组开辟空间, 并清零 */
     78         pool->threads = (pthread_t *)malloc(sizeof(pthread_t)*max_thr_num); 
     79         if (pool->threads == NULL) {
     80             printf("malloc threads fail");
     81             break;
     82         }
     83         memset(pool->threads, 0, sizeof(pthread_t)*max_thr_num);
     84 
     85         /* 队列开辟空间 */
     86         pool->task_queue = (threadpool_task_t *)malloc(sizeof(threadpool_task_t)*queue_max_size);
     87         if (pool->task_queue == NULL) {
     88             printf("malloc task_queue fail");
     89             break;
     90         }
     91 
     92         /* 初始化互斥琐、条件变量 */
     93         if (pthread_mutex_init(&(pool->lock), NULL) != 0
     94                 || pthread_mutex_init(&(pool->thread_counter), NULL) != 0
     95                 || pthread_cond_init(&(pool->queue_not_empty), NULL) != 0
     96                 || pthread_cond_init(&(pool->queue_not_full), NULL) != 0)
     97         {
     98             printf("init the lock or cond fail");
     99             break;
    100         }
    101 
    102         /* 启动 min_thr_num 个 work thread */
    103         for (i = 0; i < min_thr_num; i++) {
    104             pthread_create(&(pool->threads[i]), NULL, threadpool_thread, (void *)pool);/*pool指向当前线程池*/
    105             printf("start thread 0x%x...
    ", (unsigned int)pool->threads[i]);
    106         }
    107         pthread_create(&(pool->adjust_tid), NULL, adjust_thread, (void *)pool);/* 启动管理者线程 */
    108 
    109         return pool;
    110 
    111     } while (0);
    112 
    113     threadpool_free(pool);      /* 前面代码调用失败时,释放poll存储空间 */
    114 
    115     return NULL;
    116 }
    117 
    118 /* 向线程池中 添加一个任务 */
    119 int threadpool_add(threadpool_t *pool, void*(*function)(void *arg), void *arg)
    120 {
    121     pthread_mutex_lock(&(pool->lock));
    122 
    123     /* ==为真,队列已经满, 调wait阻塞 */
    124     while ((pool->queue_size == pool->queue_max_size) && (!pool->shutdown)) {
    125         pthread_cond_wait(&(pool->queue_not_full), &(pool->lock));
    126     }
    127     if (pool->shutdown) {
    128         pthread_mutex_unlock(&(pool->lock));
    129     }
    130 
    131     /* 清空 工作线程 调用的回调函数 的参数arg */
    132     if (pool->task_queue[pool->queue_rear].arg != NULL) {
    133         free(pool->task_queue[pool->queue_rear].arg);
    134         pool->task_queue[pool->queue_rear].arg = NULL;
    135     }
    136     /*添加任务到任务队列里*/
    137     pool->task_queue[pool->queue_rear].function = function;
    138     pool->task_queue[pool->queue_rear].arg = arg;
    139     pool->queue_rear = (pool->queue_rear + 1) % pool->queue_max_size;       /* 队尾指针移动, 模拟环形 */
    140     pool->queue_size++;
    141 
    142     /*添加完任务后,队列不为空,唤醒线程池中 等待处理任务的线程*/
    143     pthread_cond_signal(&(pool->queue_not_empty));
    144     pthread_mutex_unlock(&(pool->lock));
    145 
    146     return 0;
    147 }
    148 
    149 /* 线程池中各个工作线程 */
    150 void *threadpool_thread(void *threadpool)
    151 {
    152     threadpool_t *pool = (threadpool_t *)threadpool;
    153     threadpool_task_t task;
    154 
    155     while (true) {
    156         /* Lock must be taken to wait on conditional variable */
    157         /*刚创建出线程,等待任务队列里有任务,否则阻塞等待任务队列里有任务后再唤醒接收任务*/
    158         pthread_mutex_lock(&(pool->lock));
    159 
    160         /*queue_size == 0 说明没有任务,调 wait 阻塞在条件变量上, 若有任务,跳过该while*/
    161         while ((pool->queue_size == 0) && (!pool->shutdown)) {  
    162             printf("thread 0x%x is waiting
    ", (unsigned int)pthread_self());
    163             pthread_cond_wait(&(pool->queue_not_empty), &(pool->lock));
    164 
    165             /*清除指定数目的空闲线程,如果要结束的线程个数大于0,结束线程*/
    166             if (pool->wait_exit_thr_num > 0) {
    167                 pool->wait_exit_thr_num--;
    168 
    169                 /*如果线程池里线程个数大于最小值时可以结束当前线程*/
    170                 if (pool->live_thr_num > pool->min_thr_num) {
    171                     printf("thread 0x%x is exiting
    ", (unsigned int)pthread_self());
    172                     pool->live_thr_num--;
    173                     pthread_mutex_unlock(&(pool->lock));
    174                     pthread_exit(NULL);
    175                 }
    176             }
    177         }
    178 
    179         /*如果指定了true,要关闭线程池里的每个线程,自行退出处理*/
    180         if (pool->shutdown) {
    181             pthread_mutex_unlock(&(pool->lock));
    182             printf("thread 0x%x is exiting
    ", (unsigned int)pthread_self());
    183             pthread_exit(NULL);     /* 线程自行结束 */
    184         }
    185 
    186         /*从任务队列里获取任务, 是一个出队操作*/
    187         task.function = pool->task_queue[pool->queue_front].function;
    188         task.arg = pool->task_queue[pool->queue_front].arg;
    189 
    190         pool->queue_front = (pool->queue_front + 1) % pool->queue_max_size;       /* 出队,模拟环形队列 */
    191         pool->queue_size--;
    192 
    193         /*通知可以有新的任务添加进来*/
    194         pthread_cond_broadcast(&(pool->queue_not_full));
    195 
    196         /*任务取出后,立即将 线程池琐 释放*/
    197         pthread_mutex_unlock(&(pool->lock));
    198 
    199         /*执行任务*/ 
    200         printf("thread 0x%x start working
    ", (unsigned int)pthread_self());
    201         pthread_mutex_lock(&(pool->thread_counter));                            /*忙状态线程数变量琐*/
    202         pool->busy_thr_num++;                                                   /*忙状态线程数+1*/
    203         pthread_mutex_unlock(&(pool->thread_counter));
    204         (*(task.function))(task.arg);                                           /*执行回调函数任务*/
    205         //task.function(task.arg);                                              /*执行回调函数任务*/
    206 
    207         /*任务结束处理*/ 
    208         printf("thread 0x%x end working
    ", (unsigned int)pthread_self());
    209         pthread_mutex_lock(&(pool->thread_counter));
    210         pool->busy_thr_num--;                                       /*处理掉一个任务,忙状态数线程数-1*/
    211         pthread_mutex_unlock(&(pool->thread_counter));
    212     }
    213 
    214     pthread_exit(NULL);
    215 }
    216 
    217 /* 动态调整线程函数 */
    218 void *adjust_thread(void *threadpool)
    219 {
    220     int i;
    221     threadpool_t *pool = (threadpool_t *)threadpool;
    222     while (!pool->shutdown) {
    223 
    224         sleep(DEFAULT_TIME);                                    /*定时 对线程池管理*/
    225 
    226         pthread_mutex_lock(&(pool->lock));
    227         int queue_size = pool->queue_size;                      /* 关注 任务数 */
    228         int live_thr_num = pool->live_thr_num;                  /* 存活 线程数 */
    229         pthread_mutex_unlock(&(pool->lock));
    230 
    231         pthread_mutex_lock(&(pool->thread_counter));
    232         int busy_thr_num = pool->busy_thr_num;                  /* 忙着的线程数 */
    233         pthread_mutex_unlock(&(pool->thread_counter));
    234 
    235         /* 创建新线程 算法: 任务数大于最小线程池个数, 且存活的线程数少于最大线程个数时 如:30>=10 && 40<100*/
    236         if (queue_size >= MIN_WAIT_TASK_NUM && live_thr_num < pool->max_thr_num) {
    237             pthread_mutex_lock(&(pool->lock));  
    238             int add = 0;
    239 
    240             /*一次增加 DEFAULT_THREAD 个线程*/
    241             for (i = 0; i < pool->max_thr_num && add < DEFAULT_THREAD_VARY
    242                     && pool->live_thr_num < pool->max_thr_num; i++) {
    243                 if (pool->threads[i] == 0 || !is_thread_alive(pool->threads[i])) {
    244                     pthread_create(&(pool->threads[i]), NULL, threadpool_thread, (void *)pool);
    245                     add++;
    246                     pool->live_thr_num++;
    247                 }
    248             }
    249 
    250             pthread_mutex_unlock(&(pool->lock));
    251         }
    252 
    253         /* 销毁多余的空闲线程 算法:忙线程X2 小于 存活的线程数 且 存活的线程数 大于 最小线程数时*/
    254         if ((busy_thr_num * 2) < live_thr_num  &&  live_thr_num > pool->min_thr_num) {
    255 
    256             /* 一次销毁DEFAULT_THREAD个线程, 隨機10個即可 */
    257             pthread_mutex_lock(&(pool->lock));
    258             pool->wait_exit_thr_num = DEFAULT_THREAD_VARY;      /* 要销毁的线程数 设置为10 */
    259             pthread_mutex_unlock(&(pool->lock));
    260 
    261             for (i = 0; i < DEFAULT_THREAD_VARY; i++) {
    262                 /* 通知处在空闲状态的线程, 他们会自行终止*/
    263                 pthread_cond_signal(&(pool->queue_not_empty));
    264             }
    265         }
    266     }
    267 
    268     return NULL;
    269 }
    270 
    271 //销毁线程池
    272 int threadpool_destroy(threadpool_t *pool)
    273 {
    274     int i;
    275     if (pool == NULL) {
    276         return -1;
    277     }
    278     pool->shutdown = true;
    279 
    280     /*先销毁管理线程*/
    281     pthread_join(pool->adjust_tid, NULL);
    282 
    283     for (i = 0; i < pool->live_thr_num; i++) {
    284         /*通知所有的空闲线程*/
    285         pthread_cond_broadcast(&(pool->queue_not_empty));
    286     }
    287     for (i = 0; i < pool->live_thr_num; i++) {
    288         pthread_join(pool->threads[i], NULL);
    289     }
    290     threadpool_free(pool);
    291 
    292     return 0;
    293 }
    294 
    295 //释放线程池空间
    296 int threadpool_free(threadpool_t *pool)
    297 {
    298     if (pool == NULL) {
    299         return -1;
    300     }
    301 
    302     if (pool->task_queue) {
    303         free(pool->task_queue);
    304     }
    305     if (pool->threads) {
    306         free(pool->threads);
    307         pthread_mutex_lock(&(pool->lock));
    308         pthread_mutex_destroy(&(pool->lock));
    309         pthread_mutex_lock(&(pool->thread_counter));
    310         pthread_mutex_destroy(&(pool->thread_counter));
    311         pthread_cond_destroy(&(pool->queue_not_empty));
    312         pthread_cond_destroy(&(pool->queue_not_full));
    313     }
    314     free(pool);
    315     pool = NULL;
    316 
    317     return 0;
    318 }
    319 
    320 int threadpool_all_threadnum(threadpool_t *pool)
    321 {
    322     int all_threadnum = -1;
    323     pthread_mutex_lock(&(pool->lock));
    324     all_threadnum = pool->live_thr_num;
    325     pthread_mutex_unlock(&(pool->lock));
    326     return all_threadnum;
    327 }
    328 
    329 int threadpool_busy_threadnum(threadpool_t *pool)
    330 {
    331     int busy_threadnum = -1;
    332     pthread_mutex_lock(&(pool->thread_counter));
    333     busy_threadnum = pool->busy_thr_num;
    334     pthread_mutex_unlock(&(pool->thread_counter));
    335     return busy_threadnum;
    336 }
    337 
    338 int is_thread_alive(pthread_t tid)
    339 {
    340     int kill_rc = pthread_kill(tid, 0);     //发0号信号,测试线程是否存活
    341     if (kill_rc == ESRCH) {
    342         return false;
    343     }
    344 
    345     return true;
    346 }
    347 
    348 /*测试*/ 
    349 
    350 #if 1
    351 /* 线程池中的线程,模拟处理业务 */
    352 void *process(void *arg)
    353 {
    354     printf("thread 0x%x working on task %d
     ",(unsigned int)pthread_self(),*(int *)arg);
    355     sleep(1);
    356     printf("task %d is end
    ",*(int *)arg);
    357 
    358     return NULL;
    359 }
    360 int main(void)
    361 {
    362     /*threadpool_t *threadpool_create(int min_thr_num, int max_thr_num, int queue_max_size);*/
    363 
    364     threadpool_t *thp = threadpool_create(3,100,100);/*创建线程池,池里最小3个线程,最大100,队列最大100*/
    365     printf("pool inited");
    366 
    367     //int *num = (int *)malloc(sizeof(int)*20);
    368     int num[20], i;
    369     for (i = 0; i < 20; i++) {
    370         num[i]=i;
    371         printf("add task %d
    ",i);
    372         threadpool_add(thp, process, (void*)&num[i]);     /* 向线程池中添加任务 */
    373     }
    374     sleep(10);                                          /* 等子线程完成任务 */
    375     threadpool_destroy(thp);
    376 
    377     return 0;
    378 }
    379 
    380 #endif
    thread_pool.c
  • 相关阅读:
    20120410日常记录
    20120229
    20120307
    20120411记录
    javaScript OOP学习总结
    vue.js初学习
    SQL Server 2008 r2 新建实例无法远程访问 Error:40
    SSRS 中表格报表分组字段含有NULL
    Iframe中的SSRS报表中使用windows.open在新窗口中不能打开URL中的问题
    SSIS中的Unicode,非Unicode问题
  • 原文地址:https://www.cnblogs.com/clno1/p/12978606.html
Copyright © 2020-2023  润新知