线程池理解
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