线程池理解

Linux 多线程编程之 线程池 的原理和一个简单的C实现,提高对多线程编

程的认知,同步处理等操作,以及如何在实际项目中高效的利用多线程开

发。

 

1.  线程池介绍

为什么需要线程池???

目前的大多数网络服务器,包括Web服务器、Email服务器以及数据库服务
器等都具有一个共同点,就是单位时间内必须处理数目巨大的连接请求,
但处理时间却相对较短。

传统多线程方案中我们采用的服务器模型则是一旦接受到请求之后,即创
建一个新的线程,由该线程执行任务。任务执行完毕后,线程退出,这就
是是“即时创建,即时销毁”的策略。尽管与创建进程相比,创建线程的时
间已经大大的缩短,但是如果提交给线程的任务是执行时间较短,而且执
行次数极其频繁,那么服务器将处于不停的创建线程,销毁线程的状态,
这笔开销将是不可忽略的。

线程池为线程生命周期开销问题和资源不足问题提供了解决方案。通过对
多个任务重用线程,线程创建的开销被分摊到了多个任务上。其好处是,
因为在请求到达时线程已经存在,所以无意中也消除了线程创建所带来的
延迟。这样,就可以立即为请求服务,使应用程序响应更快。而且,通过
适当地调整线程池中的线程数目,也就是当请求的数目超过某个阈值时,
就强制其它任何新到的请求一直等待,直到获得一个线程来处理为止,从
而可以防止资源不足。

 

2. 线程池结构

2.1 线程池任务结点结构

线程池任务结点用来保存用户投递过来的的任务,并放入线程池中的线程来执行,任务结构

 1 1 // 线程池任务结点
 2 2 struct worker_t {
 3 3     void * (* process)(void * arg); /*回调函数*/
 4 4     int    paratype;                /*函数类型(预留)*/
 5 5     void * arg;                     /*回调函数参数*/
 6 6     struct worker_t * next;         /*链接下一个任务节点*/
 7 7 };
 8 
 9 2.2 线程池控制器
10 
11 线程池控制器用来对线程池进行控制管理,描述当前线程池的最基本信息,包括任务的投递,线
12 
13 程池状态的更新与查询,线程池的销毁等,其结构如下:
14 
15 /*线程控制器*/
16 struct CThread_pool_t {
17     pthread_mutex_t queue_lock;     /*互斥锁*/
18     pthread_cond_t  queue_ready;    /*条件变量*/
19     
20     worker_t * queue_head;          /*任务节点链表 保存所有投递的任务*/
21     int shutdown;                   /*线程池销毁标志 1-销毁*/
22     pthread_t * threadid;           /*线程ID*/
23     
24     int max_thread_num;             /*线程池可容纳最大线程数*/
25     int current_pthread_num;        /*当前线程池存放的线程*/
26     int current_pthread_task_num;   /*当前已经执行任务和已分配任务的线程数目和*/
27     int current_wait_queue_num;     /*当前等待队列的的任务数目*/
28     int free_pthread_num;           /*线程池允许最大的空闲线程数/*/
29     
30     /**
31      *  function:       ThreadPoolAddWorkUnlimit
32      *  description:    向线程池投递任务
33      *  input param:    pthis   线程池指针
34      *                  process 回调函数
35      *                  arg     回调函数参数
36      *  return Valr:    0       成功
37      *                  -1      失败
38      */     
39     int (* AddWorkUnlimit)(void * pthis, void * (* process)(void * arg), void * arg);
40     
41     /**
42      *  function:       ThreadPoolAddWorkLimit
43      *  description:    向线程池投递任务,无空闲线程则阻塞
44      *  input param:    pthis   线程池指针
45      *                  process 回调函数
46      *                  arg     回调函数参数
47      *  return Val:     0       成功
48      *                  -1      失败
49      */     
50     int (* AddWorkLimit)(void * pthis, void * (* process)(void * arg), void * arg);
51     
52     /**
53      *  function:       ThreadPoolGetThreadMaxNum
54      *  description:    获取线程池可容纳的最大线程数
55      *  input param:    pthis   线程池指针
56      */     
57     int (* GetThreadMaxNum)(void * pthis);
58     
59     /**
60      *  function:       ThreadPoolGetCurrentThreadNum
61      *  description:    获取线程池存放的线程数
62      *  input param:    pthis   线程池指针
63      *  return Val:     线程池存放的线程数
64      */     
65     int (* GetCurrentThreadNum)(void * pthis);
66     
67     /**
68      *  function:       ThreadPoolGetCurrentTaskThreadNum
69      *  description:    获取当前正在执行任务和已经分配任务的线程数目和
70      *  input param:    pthis   线程池指针
71      *  return Val:     当前正在执行任务和已经分配任务的线程数目和
72      */     
73     int (* GetCurrentTaskThreadNum)(void * pthis);
74     
75     /**
76      *  function:       ThreadPoolGetCurrentWaitTaskNum
77      *  description:    获取线程池等待队列任务数
78      *  input param:    pthis   线程池指针
79      *  return Val:     等待队列任务数
80      */     
81     int (* GetCurrentWaitTaskNum)(void * pthis);
82     
83     /**
84      *  function:       ThreadPoolDestroy
85      *  description:    销毁线程池
86      *  input param:    pthis   线程池指针
87      *  return Val:     0       成功
88      *                  -1      失败
89      */     
90     int (* Destroy)(void * pthis);    
91 };

 

2.3 线程池运行结构

解释:

1) 图中的线程池中的"空闲"和"执行"分别表示空闲线程和执行线程,空闲线程指在正在等待任务的线程,

 同样执行线程指正在执行任务的线程,  两者是相互转换的。当用户投递任务过来则用空闲线程来执行

 该任务,且空闲线程状态转换为执行线程;当任务执行完后,执行线程状态转变为空闲线程。

2) 创建线程池时,正常情况会创建一定数量的线程,  所有线程初始化为空闲线程,线程阻塞等待用户

 投递任务。

3) 用户投递的任务首先放入等待队列queue_head 链表中, 如果线程池中有空闲线程则放入空闲线程中

 执行,否则根据条件选择继续等待空闲线程或者新建一个线程来执行,新建的线程将放入线程池中。

4) 执行的任务会从等待队列中脱离,并在任务执行完后释放任务结点worker_t 

 

3. 线程池控制 / 部分函数解释

3.1 线程池创建

 创建 max_num 个线程 ThreadPoolRoutine,即空闲线程

 1 /**
 2  *  function:       ThreadPoolConstruct
 3  *  description:    构建线程池
 4  *  input param:    max_num   线程池可容纳的最大线程数
 5  *                  free_num  线程池允许存在的最大空闲线程,超过则将线程释放回操作系统
 6  *  return Val:     线程池指针                 
 7  */     
 8 CThread_pool_t * 
 9 ThreadPoolConstruct(int max_num, int free_num)
10 {
11     int i = 0;
12     
13     CThread_pool_t * pool = (CThread_pool_t *)malloc(sizeof(CThread_pool_t));
14     if(NULL == pool)
15         return NULL;
16     
17     memset(pool, 0, sizeof(CThread_pool_t));
18     
19     /*初始化互斥锁*/
20     pthread_mutex_init(&(pool->queue_lock), NULL);
21     /*初始化条件变量*/
22     pthread_cond_init(&(pool->queue_ready), NULL);
23     
24     pool->queue_head                = NULL;
25     pool->max_thread_num            = max_num; // 线程池可容纳的最大线程数
26     pool->current_wait_queue_num    = 0;
27     pool->current_pthread_task_num  = 0;
28     pool->shutdown                  = 0;
29     pool->current_pthread_num       = 0;
30     pool->free_pthread_num          = free_num; // 线程池允许存在最大空闲线程
31     pool->threadid                  = NULL;
32     pool->threadid                  = (pthread_t *)malloc(max_num*sizeof(pthread_t));
33     /*该函数指针赋值*/
34     pool->AddWorkUnlimit            = ThreadPoolAddWorkUnlimit;
35     pool->AddWorkLimit              = ThreadPoolAddWorkLimit;
36     pool->Destroy                   = ThreadPoolDestroy;
37     pool->GetThreadMaxNum           = ThreadPoolGetThreadMaxNum;
38     pool->GetCurrentThreadNum       = ThreadPoolGetCurrentThreadNum;
39     pool->GetCurrentTaskThreadNum   = ThreadPoolGetCurrentTaskThreadNum;
40     pool->GetCurrentWaitTaskNum     = ThreadPoolGetCurrentWaitTaskNum;
41     
42     for(i=0; i<max_num; i++) {
43         pool->current_pthread_num++;    // 当前池中的线程数
44         /*创建线程*/
45         pthread_create(&(pool->threadid[i]), NULL, ThreadPoolRoutine, (void *)pool);
46         usleep(1000);        
47     }
48     
49     return pool;
50 }

3.2 投递任务

 1 /**
 2  *  function:       ThreadPoolAddWorkLimit
 3  *  description:    向线程池投递任务,无空闲线程则阻塞
 4  *  input param:    pthis   线程池指针
 5  *                  process 回调函数
 6  *                  arg     回调函数参数
 7  *  return Val:     0       成功
 8  *                  -1      失败
 9  */     
10 int
11 ThreadPoolAddWorkLimit(void * pthis, void * (* process)(void * arg), void * arg)
12 { 
13     // int FreeThreadNum = 0;
14     // int CurrentPthreadNum = 0;
15     
16     CThread_pool_t * pool = (CThread_pool_t *)pthis;
17     
18     /*为添加的任务队列节点分配内存*/
19     worker_t * newworker  = (worker_t *)malloc(sizeof(worker_t)); 
20     if(NULL == newworker) 
21         return -1;
22     
23     newworker->process  = process;  // 回调函数,在线程ThreadPoolRoutine()中执行
24     newworker->arg      = arg;      // 回调函数参数
25     newworker->next     = NULL;      
26     
27     pthread_mutex_lock(&(pool->queue_lock));
28     
29     /*插入新任务队列节点*/
30     worker_t * member = pool->queue_head;   // 指向任务队列链表整体
31     if(member != NULL) {
32         while(member->next != NULL) // 队列中有节点
33             member = member->next;  // member指针往后移动
34             
35         member->next = newworker;   // 插入到队列链表尾部
36     } else 
37         pool->queue_head = newworker; // 插入到队列链表头
38     
39     assert(pool->queue_head != NULL);
40     pool->current_wait_queue_num++; // 等待队列加1
41     
42     /*空闲的线程= 当前线程池存放的线程 - 当前已经执行任务和已分配任务的线程数目和*/
43     int FreeThreadNum = pool->current_pthread_num - pool->current_pthread_task_num;
44     /*如果没有空闲线程且池中当前线程数不超过可容纳最大线程*/
45     if((0 == FreeThreadNum) && (pool->current_pthread_num < pool->max_thread_num)) {  //-> 条件为真进行新线程创建
46         int CurrentPthreadNum = pool->current_pthread_num;
47         
48         /*新增线程*/
49         pool->threadid = (pthread_t *)realloc(pool->threadid, 
50                                         (CurrentPthreadNum+1) * sizeof(pthread_t));
51                                         
52         pthread_create(&(pool->threadid[CurrentPthreadNum]),
53                                               NULL, ThreadPoolRoutine, (void *)pool);
54         /*当前线程池中线程总数加1*/                                   
55         pool->current_pthread_num++;
56         
57         /*分配任务线程数加1*/
58         pool->current_pthread_task_num++;
59         pthread_mutex_unlock(&(pool->queue_lock));
60         
61         /*发送信号给一个处与条件阻塞等待状态的线程*/
62         pthread_cond_signal(&(pool->queue_ready));
63         return 0;
64     }
65     
66     pool->current_pthread_task_num++;
67     pthread_mutex_unlock(&(pool->queue_lock));
68     
69     /*发送信号给一个处与条件阻塞等待状态的线程*/
70     pthread_cond_signal(&(pool->queue_ready));
71 //  usleep(10);  //看情况  
72     return 0;
73 }

投递任务时先创建一个任务结点保存回调函数和函数参数,并将任务结点放入等待队列中,在代码中

 注释"//->条件为真创建新线程",realloc() 会在保存原始内存中的数据不变的基础上新增1个sizeof(pthread_t)

 大小内存。之后更新current_pthread_num,和current_pthread_task_num;并发送信号

 pthread_cond_signal(&(pool->queue_read)),给一个处于条件阻塞等待状态的线程,即线程ThreadPoolRoutin()

 中的pthread_cond_wait(&(pool->queue_read), &(pool->queue_lock))阻塞等待接收信号,重点讲互

 斥锁和添加变量:

  pthread_mutex_t  queue_lock;   /**< 互斥锁*/

  pthread_cond_t    queue_ready;   /**< 条件变量*/ 

 这两个变量时线程池实现中很重要的点,这里简要介绍代码中会用到的相关函数功能;

 

3.3 执行线程

 1 /**
 2  *  function:       ThreadPoolRoutine
 3  *  description:    线程池中执行的线程
 4  *  input param:    arg  线程池指针
 5  */     
 6 void * 
 7 ThreadPoolRoutine(void * arg)
 8 {
 9     CThread_pool_t * pool = (CThread_pool_t *)arg;
10     
11     while(1) {
12         /*上锁,pthread_cond_wait()调用会解锁*/
13         pthread_mutex_lock(&(pool->queue_lock));
14         
15         /*队列没有等待任务*/
16         while((pool->current_wait_queue_num == 0) && (!pool->shutdown)) {
17             /*条件锁阻塞等待条件信号*/
18             pthread_cond_wait(&(pool->queue_ready), &(pool->queue_lock));
19         }
20         
21         if(pool->shutdown) {
22             pthread_mutex_unlock(&(pool->queue_lock));
23             pthread_exit(NULL);         // 释放线程
24         }
25         
26         assert(pool->current_wait_queue_num != 0);
27         assert(pool->queue_head != NULL);
28         
29         pool->current_wait_queue_num--; // 等待任务减1,准备执行任务
30         worker_t * worker = pool->queue_head;   // 去等待队列任务节点头
31         pool->queue_head = worker->next;        // 链表后移     
32         pthread_mutex_unlock(&(pool->queue_lock));
33         
34         (* (worker->process))(worker->arg);      // 执行回调函数
35         
36         pthread_mutex_lock(&(pool->queue_lock));
37         pool->current_pthread_task_num--;       // 函数执行结束
38         free(worker);   // 释放任务结点
39         worker = NULL;
40         
41         if((pool->current_pthread_num - pool->current_pthread_task_num) > pool->free_pthread_num) {
42             pthread_mutex_unlock(&(pool->queue_lock));
43             break;  // 当线程池中空闲线程超过 free_pthread_num 则将线程释放回操作系统
44         }
45         pthread_mutex_unlock(&(pool->queue_lock));    
46     }
47     
48     pool->current_pthread_num--;    // 当前线程数减1
49     pthread_exit(NULL);             // 释放线程
50     
51     return (void *)NULL;
52 }

这个就是用来执行任务的线程,在初始化创建线程时所有线程都全部阻塞在pthread_cond_wait()处

 此时的线程就为空闲线程,也就是线程被挂起,当收到信号并取得互斥锁时,     表明任务投递过来

 则获取等待队列里的任务结点并执行回调函数;  函数执行结束后回去判断当前等待队列是否还有任

 务,有则接下去执行,否则重新阻塞回到空闲线程状态。

 

4. 完整代码实现

4.1 CThreadPool.h 文件

  1 /**
  2  *  线程池头文件
  3  *
  4  **/
  5 
  6 #ifndef _CTHREADPOOL_H_
  7 #define _CTHREADPOOL_H_
  8 
  9 #include <pthread.h>
 10 
 11 /*线程池可容纳最大线程数*/
 12 #define DEFAULT_MAX_THREAD_NUM      100
 13 
 14 /*线程池允许最大的空闲线程,超过则将线程释放回操作系统*/
 15 #define DEFAULT_FREE_THREAD_NUM     10
 16 
 17 typedef struct worker_t         worker_t;
 18 typedef struct CThread_pool_t   CThread_pool_t;
 19 
 20 /*线程池任务节点*/
 21 struct worker_t {
 22     void * (* process)(void * arg); /*回调函数*/
 23     int    paratype;                /*函数类型(预留)*/
 24     void * arg;                     /*回调函数参数*/
 25     struct worker_t * next;         /*链接下一个任务节点*/
 26 };
 27 
 28 /*线程控制器*/
 29 struct CThread_pool_t {
 30     pthread_mutex_t queue_lock;     /*互斥锁*/
 31     pthread_cond_t  queue_ready;    /*条件变量*/
 32     
 33     worker_t * queue_head;          /*任务节点链表 保存所有投递的任务*/
 34     int shutdown;                   /*线程池销毁标志 1-销毁*/
 35     pthread_t * threadid;           /*线程ID*/
 36     
 37     int max_thread_num;             /*线程池可容纳最大线程数*/
 38     int current_pthread_num;        /*当前线程池存放的线程*/
 39     int current_pthread_task_num;   /*当前已经执行任务和已分配任务的线程数目和*/
 40     int current_wait_queue_num;     /*当前等待队列的的任务数目*/
 41     int free_pthread_num;           /*线程池允许最大的空闲线程数/*/
 42     
 43     /**
 44      *  function:       ThreadPoolAddWorkUnlimit
 45      *  description:    向线程池投递任务
 46      *  input param:    pthis   线程池指针
 47      *                  process 回调函数
 48      *                  arg     回调函数参数
 49      *  return Valr:    0       成功
 50      *                  -1      失败
 51      */     
 52     int (* AddWorkUnlimit)(void * pthis, void * (* process)(void * arg), void * arg);
 53     
 54     /**
 55      *  function:       ThreadPoolAddWorkLimit
 56      *  description:    向线程池投递任务,无空闲线程则阻塞
 57      *  input param:    pthis   线程池指针
 58      *                  process 回调函数
 59      *                  arg     回调函数参数
 60      *  return Val:     0       成功
 61      *                  -1      失败
 62      */     
 63     int (* AddWorkLimit)(void * pthis, void * (* process)(void * arg), void * arg);
 64     
 65     /**
 66      *  function:       ThreadPoolGetThreadMaxNum
 67      *  description:    获取线程池可容纳的最大线程数
 68      *  input param:    pthis   线程池指针
 69      */     
 70     int (* GetThreadMaxNum)(void * pthis);
 71     
 72     /**
 73      *  function:       ThreadPoolGetCurrentThreadNum
 74      *  description:    获取线程池存放的线程数
 75      *  input param:    pthis   线程池指针
 76      *  return Val:     线程池存放的线程数
 77      */     
 78     int (* GetCurrentThreadNum)(void * pthis);
 79     
 80     /**
 81      *  function:       ThreadPoolGetCurrentTaskThreadNum
 82      *  description:    获取当前正在执行任务和已经分配任务的线程数目和
 83      *  input param:    pthis   线程池指针
 84      *  return Val:     当前正在执行任务和已经分配任务的线程数目和
 85      */     
 86     int (* GetCurrentTaskThreadNum)(void * pthis);
 87     
 88     /**
 89      *  function:       ThreadPoolGetCurrentWaitTaskNum
 90      *  description:    获取线程池等待队列任务数
 91      *  input param:    pthis   线程池指针
 92      *  return Val:     等待队列任务数
 93      */     
 94     int (* GetCurrentWaitTaskNum)(void * pthis);
 95     
 96     /**
 97      *  function:       ThreadPoolDestroy
 98      *  description:    销毁线程池
 99      *  input param:    pthis   线程池指针
100      *  return Val:     0       成功
101      *                  -1      失败
102      */     
103     int (* Destroy)(void * pthis);    
104 };
105 
106 /**
107  *  function:       ThreadPoolConstruct
108  *  description:    构建线程池
109  *  input param:    max_num   线程池可容纳的最大线程数
110  *                  free_num  线程池允许存在的最大空闲线程,超过则将线程释放回操作系统
111  *  return Val:     线程池指针                 
112  */     
113 CThread_pool_t * ThreadPoolConstruct(int max_num, int free_num);
114 
115 /**
116  *  function:       ThreadPoolConstructDefault
117  *  description:    创建线程池,以默认的方式初始化,未创建线程
118  *
119  *  return Val:     线程池指针                 
120  */     
121 CThread_pool_t * ThreadPoolConstructDefault(void);
122 
123 #endif  // _CTHREADPOOL_H_

4.2 CThreadPool.c 文件

  1 /**
  2  *  线程池实现
  3  *
  4  **/
  5 
  6 #include <stdio.h>
  7 #include <stdlib.h>
  8 #include <string.h>
  9 #include <unistd.h>
 10 #include <sys/types.h>
 11 #include <pthread.h>
 12 #include <assert.h>
 13 
 14 #include "CThreadPool.h"
 15 
 16 void * ThreadPoolRoutine(void * arg); 
 17 
 18 /**
 19  *  function:       ThreadPoolAddWorkLimit
 20  *  description:    向线程池投递任务,无空闲线程则阻塞
 21  *  input param:    pthis   线程池指针
 22  *                  process 回调函数
 23  *                  arg     回调函数参数
 24  *  return Val:     0       成功
 25  *                  -1      失败
 26  */     
 27 int
 28 ThreadPoolAddWorkLimit(void * pthis, void * (* process)(void * arg), void * arg)
 29 { 
 30     // int FreeThreadNum = 0;
 31     // int CurrentPthreadNum = 0;
 32     
 33     CThread_pool_t * pool = (CThread_pool_t *)pthis;
 34     
 35     /*为添加的任务队列节点分配内存*/
 36     worker_t * newworker  = (worker_t *)malloc(sizeof(worker_t)); 
 37     if(NULL == newworker) 
 38         return -1;
 39     
 40     newworker->process  = process;  // 回调函数,在线程ThreadPoolRoutine()中执行
 41     newworker->arg      = arg;      // 回调函数参数
 42     newworker->next     = NULL;      
 43     
 44     pthread_mutex_lock(&(pool->queue_lock));
 45     
 46     /*插入新任务队列节点*/
 47     worker_t * member = pool->queue_head;   // 指向任务队列链表整体
 48     if(member != NULL) {
 49         while(member->next != NULL) // 队列中有节点
 50             member = member->next;  // member指针往后移动
 51             
 52         member->next = newworker;   // 插入到队列链表尾部
 53     } else 
 54         pool->queue_head = newworker; // 插入到队列链表头
 55     
 56     assert(pool->queue_head != NULL);
 57     pool->current_wait_queue_num++; // 等待队列加1
 58     
 59     /*空闲的线程= 当前线程池存放的线程 - 当前已经执行任务和已分配任务的线程数目和*/
 60     int FreeThreadNum = pool->current_pthread_num - pool->current_pthread_task_num;
 61     /*如果没有空闲线程且池中当前线程数不超过可容纳最大线程*/
 62     if((0 == FreeThreadNum) && (pool->current_pthread_num < pool->max_thread_num)) {
 63         int CurrentPthreadNum = pool->current_pthread_num;
 64         
 65         /*新增线程*/
 66         pool->threadid = (pthread_t *)realloc(pool->threadid, 
 67                                         (CurrentPthreadNum+1) * sizeof(pthread_t));
 68                                         
 69         pthread_create(&(pool->threadid[CurrentPthreadNum]),
 70                                               NULL, ThreadPoolRoutine, (void *)pool);
 71         /*当前线程池中线程总数加1*/                                   
 72         pool->current_pthread_num++;
 73         
 74         /*分配任务线程数加1*/
 75         pool->current_pthread_task_num++;
 76         pthread_mutex_unlock(&(pool->queue_lock));
 77         
 78         /*发送信号给一个处与条件阻塞等待状态的线程*/
 79         pthread_cond_signal(&(pool->queue_ready));
 80         return 0;
 81     }
 82     
 83     pool->current_pthread_task_num++;
 84     pthread_mutex_unlock(&(pool->queue_lock));
 85     
 86     /*发送信号给一个处与条件阻塞等待状态的线程*/
 87     pthread_cond_signal(&(pool->queue_ready));
 88 //  usleep(10);  //看情况  
 89     return 0;
 90 }
 91 
 92 /**
 93  *  function:       ThreadPoolAddWorkUnlimit
 94  *  description:    向线程池投递任务
 95  *  input param:    pthis   线程池指针
 96  *                  process 回调函数
 97  *                  arg     回调函数参数
 98  *  return Valr:    0       成功
 99  *                  -1      失败
100  */
101 int
102 ThreadPoolAddWorkUnlimit(void * pthis, void * (* process)(void * arg), void * arg)
103 {
104     // int FreeThreadNum = 0;
105     // int CurrentPthreadNum = 0;
106     
107     CThread_pool_t * pool = (CThread_pool_t *)pthis;
108     
109     /*给新任务队列节点分配内存*/
110     worker_t * newworker = (worker_t *)malloc(sizeof(worker_t));
111     if(NULL == newworker)
112         return -1;
113     
114     newworker->process  = process;  // 回调函数
115     newworker->arg      = arg;      // 回调函数参数
116     newworker->next     = NULL;
117     
118     pthread_mutex_lock(&(pool->queue_lock));
119     
120     /*新节点插入任务队列链表操作*/
121     worker_t * member = pool->queue_head;
122     if(member != NULL) {
123         while(member->next != NULL)
124             member = member->next;
125         
126         member->next = newworker;       // 插入队列链表尾部
127     } else 
128         pool->queue_head = newworker;   // 插入到头(也就是第一个节点,之前链表没有节点)
129     
130     assert(pool->queue_head != NULL);
131     pool->current_wait_queue_num++;     // 当前等待队列的的任务数目+1
132     
133     int FreeThreadNum = pool->current_pthread_num - pool->current_pthread_task_num;
134     /*只判断是否没有空闲线程*/
135     if(0 == FreeThreadNum) {
136         int CurrentPthreadNum = pool->current_pthread_num;
137         pool->threadid = (pthread_t *)realloc(pool->threadid,
138                                            (CurrentPthreadNum+1)*sizeof(pthread_t));
139         pthread_create(&(pool->threadid[CurrentPthreadNum]),NULL,
140                                         ThreadPoolRoutine, (void *)pool);
141         pool->current_pthread_num++;
142         if(pool->current_pthread_num > pool->max_thread_num)
143             pool->max_thread_num = pool->current_pthread_num;
144         
145         pool->current_pthread_task_num++;
146         pthread_mutex_unlock(&(pool->queue_lock));
147         pthread_cond_signal(&(pool->queue_ready));
148         return 0;
149     }
150     
151     pool->current_pthread_task_num++;
152     pthread_mutex_unlock(&(pool->queue_lock));
153     pthread_cond_signal(&(pool->queue_ready));
154 //  usleep(10);    
155     return 0;   
156 }
157 
158 /**
159  *  function:       ThreadPoolGetThreadMaxNum
160  *  description:    获取线程池可容纳的最大线程数
161  *  input param:    pthis   线程池指针
162  *  return val:     线程池可容纳的最大线程数
163  */     
164 int
165 ThreadPoolGetThreadMaxNum(void * pthis)
166 {
167     int num = 0;   
168     CThread_pool_t * pool = (CThread_pool_t *)pthis;
169     
170     pthread_mutex_lock(&(pool->queue_lock));
171     num = pool->max_thread_num;
172     pthread_mutex_unlock(&(pool->queue_lock));
173     
174     return num;
175 }
176 
177 /**
178  *  function:       ThreadPoolGetCurrentThreadNum
179  *  description:    获取线程池存放的线程数
180  *  input param:    pthis   线程池指针
181  *  return Val:     线程池存放的线程数
182  */     
183 int 
184 ThreadPoolGetCurrentThreadNum(void * pthis)
185 {
186     int num = 0;
187     CThread_pool_t * pool = (CThread_pool_t *)pthis;
188     
189     pthread_mutex_lock(&(pool->queue_lock));
190     num = pool->current_pthread_num;
191     pthread_mutex_unlock(&(pool->queue_lock));
192     
193     return num;       
194 }
195 
196 /**
197  *  function:       ThreadPoolGetCurrentTaskThreadNum
198  *  description:    获取当前正在执行任务和已经分配任务的线程数目和
199  *  input param:    pthis   线程池指针
200  *  return Val:     当前正在执行任务和已经分配任务的线程数目和
201  */   
202 int
203 ThreadPoolGetCurrentTaskThreadNum(void * pthis)
204 {
205     int num = 0;
206     CThread_pool_t * pool = (CThread_pool_t *)pthis;
207     
208     pthread_mutex_lock(&(pool->queue_lock));
209     num = pool->current_pthread_task_num;
210     pthread_mutex_unlock(&(pool->queue_lock));
211     
212     return num;   
213 }
214 
215 /**
216  *  function:       ThreadPoolGetCurrentWaitTaskNum
217  *  description:    获取线程池等待队列任务数
218  *  input param:    pthis   线程池指针
219  *  return Val:     等待队列任务数
220  */     
221 int
222 ThreadPoolGetCurrentWaitTaskNum(void * pthis)
223 {
224     int num = 0;
225     CThread_pool_t * pool = (CThread_pool_t *)pthis;
226     
227     pthread_mutex_lock(&(pool->queue_lock));
228     num = pool->current_wait_queue_num;
229     pthread_mutex_unlock(&(pool->queue_lock));
230     
231     return num;   
232 }
233 
234 /**
235  *  function:       ThreadPoolDestroy
236  *  description:    销毁线程池
237  *  input param:    pthis   线程池指针
238  *  return Val:     0       成功
239  *                  -1      失败
240  */     
241 int
242 ThreadPoolDestroy(void * pthis)
243 {
244     int i;
245     CThread_pool_t * pool = (CThread_pool_t *)pthis;
246     
247     if(pool->shutdown)      // 已销毁
248         return -1;
249         
250     pool->shutdown = 1;     // 销毁标志置位
251     
252     /*唤醒所有pthread_cond_wait()等待线程*/
253     pthread_cond_broadcast(&(pool->queue_ready));
254     for(i=0; i<pool->current_pthread_num; i++)
255         pthread_join(pool->threadid[i], NULL);  // 等待所有线程执行结束
256     
257     free(pool->threadid);   // 释放
258        
259     /*销毁任务队列链表*/
260     worker_t * head = NULL;
261     while(pool->queue_head != NULL) {
262         head = pool->queue_head;
263         pool->queue_head = pool->queue_head->next;
264         free(head);    
265     }
266     
267     /*销毁锁*/
268     pthread_mutex_destroy(&(pool->queue_lock));
269     pthread_cond_destroy(&(pool->queue_ready));
270     
271     free(pool);
272     pool = NULL;
273     
274     return 0;
275 }
276 
277 /**
278  *  function:       ThreadPoolRoutine
279  *  description:    线程池中运行的线程
280  *  input param:    arg  线程池指针
281  */     
282 void * 
283 ThreadPoolRoutine(void * arg)
284 {
285     CThread_pool_t * pool = (CThread_pool_t *)arg;
286     
287     while(1) {
288         /*上锁,pthread_cond_wait()调用会解锁*/
289         pthread_mutex_lock(&(pool->queue_lock));
290         
291         /*队列没有等待任务*/
292         while((pool->current_wait_queue_num == 0) && (!pool->shutdown)) {
293             /*条件锁阻塞等待条件信号*/
294             pthread_cond_wait(&(pool->queue_ready), &(pool->queue_lock));
295         }
296         
297         if(pool->shutdown) {
298             pthread_mutex_unlock(&(pool->queue_lock));
299             pthread_exit(NULL);         // 释放线程
300         }
301         
302         assert(pool->current_wait_queue_num != 0);
303         assert(pool->queue_head != NULL);
304         
305         pool->current_wait_queue_num--; // 等待任务减1,准备执行任务
306         worker_t * worker = pool->queue_head;   // 去等待队列任务节点头
307         pool->queue_head = worker->next;        // 链表后移     
308         pthread_mutex_unlock(&(pool->queue_lock));
309         
310         (* (worker->process))(worker->arg);      // 执行回调函数
311         
312         pthread_mutex_lock(&(pool->queue_lock));
313         pool->current_pthread_task_num--;       // 函数执行结束
314         free(worker);   // 释放任务结点
315         worker = NULL;
316         
317         if((pool->current_pthread_num - pool->current_pthread_task_num) > pool->free_pthread_num) {
318             pthread_mutex_unlock(&(pool->queue_lock));
319             break;  // 当线程池中空闲线程超过 free_pthread_num 则将线程释放回操作系统
320         }
321         pthread_mutex_unlock(&(pool->queue_lock));    
322     }
323     
324     pool->current_pthread_num--;    // 当前线程数减1
325     pthread_exit(NULL);             // 释放线程
326     
327     return (void *)NULL;
328 }
329 
330 /**
331  *  function:       ThreadPoolConstruct
332  *  description:    构建线程池
333  *  input param:    max_num   线程池可容纳的最大线程数
334  *                  free_num  线程池允许存在的最大空闲线程,超过则将线程释放回操作系统
335  *  return Val:     线程池指针                 
336  */     
337 CThread_pool_t * 
338 ThreadPoolConstruct(int max_num, int free_num)
339 {
340     int i = 0;
341     
342     CThread_pool_t * pool = (CThread_pool_t *)malloc(sizeof(CThread_pool_t));
343     if(NULL == pool)
344         return NULL;
345     
346     memset(pool, 0, sizeof(CThread_pool_t));
347     
348     /*初始化互斥锁*/
349     pthread_mutex_init(&(pool->queue_lock), NULL);
350     /*初始化条件变量*/
351     pthread_cond_init(&(pool->queue_ready), NULL);
352     
353     pool->queue_head                = NULL;
354     pool->max_thread_num            = max_num; // 线程池可容纳的最大线程数
355     pool->current_wait_queue_num    = 0;
356     pool->current_pthread_task_num  = 0;
357     pool->shutdown                  = 0;
358     pool->current_pthread_num       = 0;
359     pool->free_pthread_num          = free_num; // 线程池允许存在最大空闲线程
360     pool->threadid                  = NULL;
361     pool->threadid                  = (pthread_t *)malloc(max_num*sizeof(pthread_t));
362     /*该函数指针赋值*/
363     pool->AddWorkUnlimit            = ThreadPoolAddWorkUnlimit;
364     pool->AddWorkLimit              = ThreadPoolAddWorkLimit;
365     pool->Destroy                   = ThreadPoolDestroy;
366     pool->GetThreadMaxNum           = ThreadPoolGetThreadMaxNum;
367     pool->GetCurrentThreadNum       = ThreadPoolGetCurrentThreadNum;
368     pool->GetCurrentTaskThreadNum   = ThreadPoolGetCurrentTaskThreadNum;
369     pool->GetCurrentWaitTaskNum     = ThreadPoolGetCurrentWaitTaskNum;
370     
371     for(i=0; i<max_num; i++) {
372         pool->current_pthread_num++;    // 当前池中的线程数
373         /*创建线程*/
374         pthread_create(&(pool->threadid[i]), NULL, ThreadPoolRoutine, (void *)pool);
375         usleep(1000);        
376     }
377     
378     return pool;
379 }
380 
381 /**
382  *  function:       ThreadPoolConstructDefault
383  *  description:    创建线程池,以默认的方式初始化,未创建线程
384  *
385  *  return Val:     线程池指针                 
386  */     
387 CThread_pool_t * 
388 ThreadPoolConstructDefault(void)
389 {
390     CThread_pool_t * pool = (CThread_pool_t *)malloc(sizeof(CThread_pool_t));
391     if(NULL == pool)
392         return NULL;
393     
394     memset(pool, 0, sizeof(CThread_pool_t));
395     
396     pthread_mutex_init(&(pool->queue_lock), NULL);
397     pthread_cond_init(&(pool->queue_ready), NULL);
398     
399     pool->queue_head                = NULL;
400     pool->max_thread_num            = DEFAULT_MAX_THREAD_NUM; // 默认值
401     pool->current_wait_queue_num    = 0;
402     pool->current_pthread_task_num  = 0;
403     pool->shutdown                  = 0;
404     pool->current_pthread_num       = 0;
405     pool->free_pthread_num          = DEFAULT_FREE_THREAD_NUM; // 默认值
406     pool->threadid                  = NULL;
407     /*该函数指针赋值*/
408     pool->AddWorkUnlimit            = ThreadPoolAddWorkUnlimit;
409     pool->AddWorkLimit              = ThreadPoolAddWorkLimit;
410     pool->Destroy                   = ThreadPoolDestroy;
411     pool->GetThreadMaxNum           = ThreadPoolGetThreadMaxNum;
412     pool->GetCurrentThreadNum       = ThreadPoolGetCurrentThreadNum;
413     pool->GetCurrentTaskThreadNum   = ThreadPoolGetCurrentTaskThreadNum;
414     pool->GetCurrentWaitTaskNum     = ThreadPoolGetCurrentWaitTaskNum;
415     
416     return pool;
417 }

4.3 测试 main.c 文件

  1 #include <stdio.h> 
  2 #include <stdlib.h> 
  3 #include <unistd.h> 
  4 #include <sys/types.h> 
  5 #include <pthread.h> 
  6 #include <assert.h> 
  7 #include <string.h>
  8 
  9 #include "CThreadPool.h"
 10 
 11 
 12 void * thread_1(void * arg);
 13 void * thread_2(void * arg);
 14 void * thread_3(void * arg);
 15 void DisplayPoolStatus(CThread_pool_t * pPool);
 16 
 17 int nKillThread = 0;
 18 
 19 int main()
 20 {
 21     CThread_pool_t * pThreadPool = NULL;
 22     
 23     pThreadPool = ThreadPoolConstruct(5, 1);
 24     int nNumInput = 5;
 25     char LogInput[] = "OK!";
 26 
 27     DisplayPoolStatus(pThreadPool);
 28     /*可用AddWorkLimit()替换看执行的效果*/
 29     pThreadPool->AddWorkUnlimit((void *)pThreadPool, (void *)thread_1, (void *)NULL);
 30     /*
 31      * 没加延迟发现连续投递任务时pthread_cond_wait()会收不到信号pthread_cond_signal() !!
 32      * 因为AddWorkUnlimit()进去后调用pthread_mutex_lock()把互斥锁锁上,导致pthread_cond_wait()
 33      * 收不到信号!!也可在AddWorkUnlimit()里面加个延迟,一般情况可能也遇不到这个问题
 34      */
 35     usleep(10);    
 36     pThreadPool->AddWorkUnlimit((void *)pThreadPool, (void *)thread_2, (void *)nNumInput);
 37     usleep(10);
 38     pThreadPool->AddWorkUnlimit((void *)pThreadPool, (void *)thread_3, (void *)LogInput);
 39     usleep(10);
 40     DisplayPoolStatus(pThreadPool);
 41 
 42     nKillThread = 1;
 43     usleep(100);    /**< 先让线程退出 */
 44     DisplayPoolStatus(pThreadPool);
 45     nKillThread = 2;
 46     usleep(100);
 47     DisplayPoolStatus(pThreadPool);
 48     nKillThread = 3;
 49     usleep(100);
 50     DisplayPoolStatus(pThreadPool);
 51 
 52     pThreadPool->Destroy((void*)pThreadPool);
 53     return 0;
 54 }
 55 
 56 void * 
 57 thread_1(void * arg)
 58 {
 59     printf("Thread 1 is running !\n");
 60     while(nKillThread != 1)
 61         usleep(10);
 62     return NULL;
 63 }
 64 
 65 void * 
 66 thread_2(void * arg)
 67 {
 68     int nNum = (int)arg;
 69     
 70     printf("Thread 2 is running !\n");
 71     printf("Get Number %d\n", nNum);
 72     while(nKillThread != 2)
 73         usleep(10);
 74     return NULL;
 75 }
 76 
 77 void * 
 78 thread_3(void * arg)
 79 {
 80     char * pLog = (char *)arg;
 81     
 82     printf("Thread 3 is running !\n");
 83     printf("Get String %s\n", pLog);
 84     while(nKillThread != 3)
 85         usleep(10);
 86     return NULL;
 87 }
 88 
 89 void 
 90 DisplayPoolStatus(CThread_pool_t * pPool)
 91 {
 92     static int nCount = 1;
 93     
 94     printf("****************************\n");
 95     printf("nCount = %d\n", nCount++);
 96     printf("max_thread_num = %d\n", pPool->GetThreadMaxNum((void *)pPool));
 97     printf("current_pthread_num = %d\n", pPool->GetCurrentThreadNum((void *)pPool));
 98     printf("current_pthread_task_num = %d\n", pPool->GetCurrentTaskThreadNum((void *)pPool));
 99     printf("current_wait_queue_num = %d\n", pPool->GetCurrentWaitTaskNum((void *)pPool));
100     printf("****************************\n");
101 }

4.4 Makefile

简单写一个makefile

 1 CC = gcc
 2 CFLAGS = -g -Wall -o2
 3 LIB = -lpthread
 4 
 5 RUNE = $(CC) $(CFLAGS) $(object) -o $(exe) $(LIB)
 6 RUNO = $(CC) $(CFLAGS) -c $< -o $@ $(LIB)
 7 
 8 .RHONY:clean
 9 
10 
11 object = main.o CThreadPool.o
12 exe = CThreadpool
13 
14 $(exe):$(object)
15     $(RUNE)
16 
17 %.o:%.c CThreadPool.h
18     $(RUNO)
19 %.o:%.c
20     $(RUNO)
21 
22 
23 clean:
24     -rm -rf *.o CThreadpool *~

注意:使用模式规则,能引入用户自定义变量,为多个文件建立相同的规则,规则中的相关

 文件前必须用“%”表明。关于Makefile的一些规则解释见另一篇

 

转:https://www.cnblogs.com/zhaoosheLBJ/p/9337291.html

posted @ 2020-02-15 19:09  坚持,每天进步一点点  阅读(460)  评论(0编辑  收藏  举报