1. 線程池基本原理
2. 線程池C語言實現
2.1 線程池的數據結構
#include <stdio.h>
#include <pthread.h>
#include <stdlib.h>
#include <string.h>
#include <signal.h>
#include <errno.h>
#include <unistd.h>
typedef struct {
void *(*function)(void *); /*函數指針,回調函數*/
void *arg; /*上面函數的參數*/
} threadpool_task_t; /*各子線程任務結構體*/
typedef struct threadpool_s {
pthread_mutex_t lock; /*用於鎖住本結構體*/
pthread_mutex_t thread_counter; /*記錄忙狀態線程個數的鎖 -- busy_thread_num*/
pthread_cond_t queue_not_full; /*當隊列任務滿時,添加任務的線程阻塞,等待此條件變量*/
pthread_cond_t queue_not_empty; /*任務隊列不爲空時,通知等待任務的線程*/
pthread_t *workers_tid; /*存放線程池中每個線程的tid,數組*/
pthread_t manager_tid; /*存管理線程tid*/
threadpool_task_t *task_queue; /*任務隊列*/
int min_thread_num; /*線程池最小線程數*/
int max_thread_num; /*線程池最大線程數*/
int live_thread_num; /*當前存活線程個數*/
int busy_thread_num; /*忙線程個數*/
int wait_exit_thr_num; /*要銷燬的線程個數*/
int queue_front; /*task_queue隊頭下表*/
int queue_rear; /*task_queue隊尾下表*/
int queue_size; /*task_queue隊列中實際任務數*/
int queue_max_size; /*task_queue隊列可容納任務上限*/
int shutdown; /*標誌位,線程池使用狀態,true或者false*/
} threadpool_t;
2.2 線程池的創建
/***************************************************************************
* 創建線程池
*函數名:
* threadpool_create()
*參 數:
* min_thread_num :線程池中最小線程數量
* max_thread_num :線程池中最大線程數量
* queue_max_size :任務隊列的最大長度
*作 用:
* 創建一個指定大小的線程池
*內 容:
* 1)線程池基本參數
* 2)工作線程
* 3)管理線程
* 4)任務隊列
* 5)互斥鎖、條件變量
****************************************************************************/
threadpool_t *threadpool_create(int min_thread_num, int max_thread_num, int queue_max_size)
{
int i;
threadpool_t *pool = NULL;
do {
pool = (threadpool_t *)malloc(sizeof(threadpool_t));
if (pool == NULL) {
printf("malloc threadpool fail\n");
goto err_1;
}
pool->min_thread_num = min_thread_num;
pool->max_thread_num = max_thread_num;
pool->busy_thread_num = 0;
pool->live_thread_num = min_thread_num;
pool->queue_size = 0;
pool->queue_max_size = queue_max_size;
pool->queue_front = 0;
pool->queue_rear = 0;
pool->shutdown = 0;
/*根據最大線程上限數,給工作線程數據開闢空間,並清零*/
pool->workers_tid = (pthread_t *)malloc(sizeof(pthread_t) * max_thread_num);
if (pool->workers_tid == NULL) {
printf("malloc workers_tid fail\n");
goto err_2;
}
memset(pool->workers_tid, 0, sizeof(pthread_t) * max_thread_num);
/* 隊列開闢空間 */
pool->task_queue = (threadpool_task_t *)malloc(sizeof(threadpool_task_t) * queue_max_size);
if (pool->task_queue == NULL) {
printf("malloc task_queue fail\n");
goto err_3;
}
/* 初始化互斥鎖,條件變量 */
if (pthread_mutex_init(&(pool->lock), NULL) != 0 ||
pthread_mutex_init(&(pool->thread_counter), NULL) != 0 ||
pthread_cond_init(&(pool->queue_not_empty), NULL) != 0 ||
pthread_cond_init(&(pool->queue_not_full), NULL) != 0) {
printf("init the lock or cond fail\n");
goto err_4;
}
/* 啓動 min_thread_num 個 work thread */
for (i = 0; i < min_thread_num; i++) {
pthread_create(&(pool->workers_tid[i]), NULL, workers_thread,(void *)pool); /*pool指向當前線程池*/
printf("start thread 0x%x...\n", (unsigned int)pool->workers_tid[i]);
}
/*創建管理者線程*/
pthread_create(&(pool->manager_tid), NULL, manager_thread, (void *)pool);
} while(0);
return pool;
//threadpool_free(pool); /*前面代碼調用失敗,釋放poll存儲空間*/
err_4:
/*需要銷燬互斥鎖和條件變量*/
free(pool->task_queue);
err_3:
free(pool->workers_tid);
err_2:
free(pool);
err_1:
return NULL;
}
2.3 管理線程處理函數
#define DEFAULT_TIME 60
#define MIN_WAIT_TASK_NUM 10
#define DEFAULT_THREAD_VERY 5
/***************************************************************************
* 管理者線程
*函數名:
* manager_thread()
*參 數:
* threadpool : 使用的線程池
*作 用:
* 根據任務的數量動態調整線程池大小
*內 容:
* 1)獲取當前線程池中存在的線程和任務隊列中積累的任務
* 2)根據需求動態調整線程池中的線程的數量
*缺 點:
* 使用了太多的互斥鎖和條件變量,效率上值得商榷
****************************************************************************/
void *manager_thread(void *threadpool)
{
threadpool_t *pool = (threadpool_t *)threadpool;
int i;
while (!pool->shutdown) {
sleep(DEFAULT_TIME); /*定時對線程池管理*/
pthread_mutex_lock(&(pool->lock));
int queue_size = pool->queue_size;
int live_thread_num = pool->live_thread_num;/*線程池中存在的線程數量*/
pthread_mutex_unlock(&(pool->lock));
pthread_mutex_lock(&(pool->thread_counter));
int busy_thread_num = pool->busy_thread_num;
pthread_mutex_unlock(&(pool->thread_counter));
/* 創建新線程算法,任務數大於最小線程池個數,
* 且存活的線程數小於最大線程數時
*/
if (queue_size >= MIN_WAIT_TASK_NUM && live_thread_num < pool->max_thread_num){
pthread_mutex_lock(&(pool->lock));
int add = 0;
/* 一次增加 DEFAULT_THREAD_VERY 個線程*/
for (i = 0; i < pool->max_thread_num && add < DEFAULT_THREAD_VERY
&& pool->live_thread_num < pool->max_thread_num; i++) {
if (pool->workers_tid[i] == 0 || !is_thread_alive(pool->workers_tid[i])) {
pthread_create(&(pool->workers_tid[i]), NULL, workers_thread,(void *)pool);
add++;
pool->live_thread_num++;
}
}
pthread_mutex_unlock(&(pool->lock));
}
/* 銷燬多餘的空閒線程算法,忙線程 x2 小於存活的線程數 且
* 存活的線程數大於最小線程數時
*/
if (busy_thread_num * 2 < live_thread_num && live_thread_num > pool->min_thread_num) {
/*一次銷燬 DEFAULT_THREAD_VERY 個線程*/
pthread_mutex_lock(&(pool->lock));
pool->wait_exit_thr_num = DEFAULT_THREAD_VERY;
pthread_mutex_unlock(&(pool->lock));
for (i = 0; i < DEFAULT_THREAD_VERY; i++) {
/*通知處在空閒狀態的線程,他們會自行終止,線程自殺*/
pthread_cond_signal(&(pool->queue_not_empty));
}
}
}
return NULL;
}
2.4 工作者線程處理函數
/***************************************************************************
* 工作線程處理函數
*函數名:
* workers_thread()
*參 數:
* threadpool :包含線程池中所有的參數
*作 用:
* 等待分配任務並執行之
*內 容:
* 1)睡眠等待分配任務
* 2)是否終結本線程
* 3)從任務隊列上取任務,更改線程busy狀態
* 4)執行任務
* 5)恢復爲空閒狀態
*缺 點:
* 使用了太多的互斥鎖和條件變量,效率上值得商榷
****************************************************************************/
void *workers_thread(void *threadpool)
{
threadpool_t *pool = (threadpool_t *)thr eadpool;
threadpool_task_t task;
while(1) {
/* Lock must be taken to wait on condition variable */
/* 剛創建出線程,等待任務隊列裏面有任務,否則阻塞等待任務隊列裏有任務再喚醒
* 接收任務
*/
pthread_mutex_lock(&(pool->lock));
/* queue_size == 0 說明沒有任務,調wait 阻塞在條件變量上,若有任務,跳過該while */
while((pool->queue_size == 0) && (!pool->shutdown)) {
printf("Workers'thread ID 0x%x is waiting\n", (unsigned int)pthread_self());
pthread_cond_wait(&(pool->queue_not_empty), &(pool->lock));
/* 清除指定數目的空閒線程,如果要結束的線程個數大於0,結束線程 */
if (pool->wait_exit_thr_num > 0) {
/* 如果線程池裏的線程個數大於最小值時可以結束當前線程 */
if (pool->live_thread_num > pool->min_thread_num) {
printf("Workers'thread ID 0x%x is exiting\n", (unsigned int)pthread_self());
pool->live_thread_num--;
pool->wait_exit_thr_num--;
pthread_mutex_unlock(&(pool->lock));
pthread_exit(NULL);
}
}
}
/*如果關閉了線程池,自行退出處理*/
if (pool->shutdown == 1) {
printf("Workers'thread ID 0x%x is exiting\n", (unsigned int)pthread_self());
pthread_mutex_unlock(&(pool->lock));
pthread_exit(NULL);
}
/*從任務隊列裏獲取任務,是一個出隊操作*/
task.function = pool->task_queue[pool->queue_front].function;
task.arg = pool->task_queue[pool->queue_front].arg;
/*出隊,模擬環形隊列*/
pool->queue_front = (pool->queue_front + 1) % pool->queue_max_size;
pool->queue_size--;
/*通知可以有新的任務添加進來*/
pthread_cond_broadcast(&(pool->queue_not_full));
/*任務取出後,立即將線程池鎖釋放*/
pthread_mutex_unlock(&(pool->lock));
/*設置當前線程忙狀態*/
pthread_mutex_lock(&(pool->thread_counter)); /*忙狀態線程數變量鎖*/
pool->busy_thread_num++; /*忙狀態線程數+1*/
pthread_mutex_unlock(&(pool->thread_counter));
/*執行任務*/
(*(task.function))(task.arg);
/*由忙狀態切換爲空閒狀態*/
pthread_mutex_lock(&(pool->thread_counter));
pool->busy_thread_num--;
pthread_mutex_unlock(&(pool->thread_counter));
}
return NULL;
}
2.5 任務的添加
/***************************************************************************
* 向線程池的任務隊列中添加一個任務
*函數名:
* threadpool_add()
*參 數:
* pool : 使用的線程池
* function :任務的執行函數
* arg :任務執行參數的參數
*作 用:
* 向線程池的任務隊列中添加一個任務
*內 容:
* 1)任務隊列是否已滿
* 2)添加任務
* 3)喚醒在任務隊列上睡眠的線程
*缺 點:
* 使用了太多的互斥鎖和條件變量,效率上值得商榷
****************************************************************************/
int threadpool_add(threadpool_t *pool, void *function(void *arg), void *arg)
{
pthread_mutex_lock(&(pool->lock));
/*爲真,隊列已滿,調wait等待*/
while ((pool->queue_size == pool->queue_max_size) && (!pool->shutdown)) {
pthread_cond_wait(&(pool->queue_not_empty), &(pool->lock));
}
if (pool->shutdown) {
pthread_mutex_unlock(&(pool->lock));
return 0;
}
/*清空工作線程 調用回調函數的參數 arg*/
if (pool->task_queue[pool->queue_rear].arg != NULL) {
free(pool->task_queue[pool->queue_rear].arg);
pool->task_queue[pool->queue_rear].arg = NULL;
}
/*添加任務到任務隊列裏面*/
pool->task_queue[pool->queue_rear].function = function;
pool->task_queue[pool->queue_rear].arg = arg;
pool->queue_rear = (pool->queue_rear + 1) % pool->queue_max_size;
pool->queue_size++;
/*添加完任務後,隊列不爲空,喚醒線程池中等待處理任務的線程*/
pthread_cond_signal(&(pool->queue_not_empty));
pthread_mutex_unlock(&(pool->lock));
return 0;
}
2.6 線程池的銷燬
/***************************************************************************
* 銷燬線程池
*函數名:
* threadpool_distory()
*參 數:
* threadpool : 要銷燬的線程池
*作 用:
* 銷燬線程池
*內 容:
* 1)發送任務,銷燬線程
* 2)回收線程資源
*缺 點:
* 無
****************************************************************************/
int threadpool_distory(threadpool_t *pool)
{
int i;
if (pool == NULL) {
return -1;
}
pool->shutdown = 1;
/*先銷燬管理線程*/
pthread_join(pool->manager_tid, NULL);
for (i = 0; i < pool->live_thread_num; i++) {/*通知所有空閒線程*/
pthread_cond_broadcast(&(pool->queue_not_empty));
}
for (i = 0; i < pool->live_thread_num; i++) {/*回收所有管理者線程資源*/
pthread_join(pool->workers_tid[i], NULL);
}
threadpool_free(pool);
return 0;
}
/***************************************************************************
* 釋放線程池資源
*函數名:
* threadpool_free()
*參 數:
* threadpool : 要釋放的線程池
*作 用:
* 釋放線程池資源
*內 容:
* 1)釋放任務隊列
* 2)銷燬互斥鎖和條件變量
* 3)釋放線程池
*缺 點:
* 無
****************************************************************************/
int threadpool_free(threadpool_t *pool)
{
if (pool == NULL) {
printf("thread pool is already free\n");
return -1;
}
if (pool->task_queue) {
free(pool->task_queue);
}
if (pool->workers_tid) {
free(pool->workers_tid);
pthread_mutex_lock(&(pool->lock));
pthread_mutex_destroy(&(pool->lock));
pthread_mutex_lock(&(pool->thread_counter));
pthread_mutex_destroy(&(pool->thread_counter));
pthread_cond_destroy(&(pool->queue_not_empty));
pthread_cond_destroy(&(pool->queue_not_full));
}
free(pool);
pool = NULL;
return 0;
}
2.7 其他接口
/***************************************************************************
* 判斷當前線程是否存在
*函數名:
* is_thread_alive()
*參 數:
* tid : 待查詢的線程PID
*作 用:
* 判斷當前線程是否存在
*內 容:
* 1)發送0信號來判斷
*缺 點:
* 無
****************************************************************************/
int is_thread_alive(pthread_t tid)
{
int kill_rc = pthread_kill(tid, 0); /*發0號信號,測試線程是否存活*/
if (kill_rc == ESRCH) {
return 0;
}
return 1;
}
2.9 測試demo
/***************************************************************************
* 以下爲測試demo
*函數名:
* process()
*參 數:
* arg : 任務參數
*作 用:
* 任務處理函數
*內 容:
* 1)執行任務
*缺 點:
* 無
****************************************************************************/
/* 線程池中的線程,模擬處理業務 */
void process(void *arg)
{
printf("thread 0x%x working on task %d\n", (unsigned int)pthread_self(),
*(int *)arg);
sleep(1);
printf("task %d is end\n", *(int *)arg);
}
int main(int argc, char **argv)
{
int num[20], i;
/*threadpool_t *threadpool_create(int min_thread_num, int max_thread_num, int queue_max_size)*/
/* 創建線程池,池裏最小3個線程,最大100,隊列最大100 */
threadpool_t *thp = threadpool_create(3, 100, 100);
if (thp == NULL) {
printf("threadpool_create fail\n");
return 0;
}
printf("pool init\n");
for (i = 0; i < 20; i++) {
num[i] = i;
printf("add task %d\n", i);
/* 向線程池中添加任務 */
threadpool_add(thp, (void *)&process, (void *)&num[i]);
}
/*等待子線程完成任務*/
sleep(30);
threadpool_distory(thp);
return 0;
}
代碼實現參考文章:
linux進程間通信—本地socket套接字(六)—線程池原理及C語言實現線程池
經典文章:
另一篇不錯的線程池文章