线程池理解

久未见 提交于 2020-02-15 19:43:32

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

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!