基本功能
1. 實現一個執行緒的佇列,佇列中的執行緒啟動後不再釋放;
2. 沒有任務執行時,執行緒處於pending狀態,等待喚醒,不佔cpu;
3. 當有任務需要執行時,從執行緒佇列中取出一個執行緒執行任務;
4. 任務執行完成後執行緒再次進入pending狀態,等待喚醒;
擴充套件功能
1. 執行緒的佇列大小可設定;
2. 最大可建立的執行緒數可設定;
3. 根據執行需求,按需步進啟動執行緒,避免大量執行緒一直處於pending狀態,佔用資源;
關鍵程式碼分析
資料結構
1 /* 執行緒執行的任務引數 */ 2 typedef struct 3 { 4 void (*func)(void*, void*); /* 任務函式指標 */ 5 void *arg1; /* 任務函式第一個引數 */ 6 void *arg2; /* 任務函式第二個引數 */ 7 }tThreadTaskInfo; 8 9 /* 執行緒池引數 */ 10 typedef struct 11 { 12 pthread_mutex_t lock; /* 執行緒池互斥鎖 */ 13 pthread_cond_t cond; /* 執行緒池同步訊號 */ 14 15 pthread_t *threads; /* 儲存執行緒池建立的所有執行緒 */ 16 int32_t threadMaxNum; /* 最大可建立執行緒數 */ 17 int32_t threadStartStep; /* 一次啟動執行緒的個數 */ 18 int32_t threadStartCnt; /* 已啟動執行緒個數 */ 19 int32_t threadPendCnt; /* 已啟動但是處於Pending狀態的執行緒 */ 20 21 tThreadTaskInfo *taskQueue; /* 等待執行的任務佇列 */ 22 int32_t taskQueueSize; /* 任務佇列的大小 */ 23 int32_t taskQueueHead; /* 當前任務佇列頭索引 */ 24 int32_t taskQueueTail; /* 當前任務佇列尾索引 */ 25 int32_t taskPendCnt; /* 等待執行的任務個數 */ 26 27 int32_t isShutdown; /* 執行緒池正在關閉 */ 28 }tThreadpoolInfo;
建立執行緒池
- 建立執行緒池時只分配了儲存pthread_t的空間,但是不啟動執行緒,後面根據需求步進啟動;
1 /************************************ 2 * 建立執行緒池 3 * 4 * @threadMaxNum -- 最大可建立執行緒個數 5 * @threadStartStep -- 一次啟動執行緒的個數 6 * @taskQueueSize -- 任務佇列的大小 7 * 8 * @Retuen -- 成功:執行緒池的引用 9 * 失敗:NULL 10 * **********************************/ 11 tThreadpoolInfo* threadpool_create( 12 int32_t threadMaxNum, 13 int32_t threadStartStep, 14 int32_t taskQueueSize) 15 { 16 tThreadpoolInfo *threadpool = NULL; 17 18 if ((0 >= threadMaxNum) 19 || (0 >= threadStartStep) 20 || (0 >= taskQueueSize)) 21 { 22 THREADPOOL_ERR("invalid param.\r\n"); 23 goto error_exit; 24 } 25 26 threadpool = (tThreadpoolInfo *)malloc(sizeof(tThreadpoolInfo)); 27 if (NULL == threadpool) 28 { 29 THREADPOOL_ERR("malloc threadpool failed.\r\n"); 30 goto error_exit; 31 } 32 33 memset(threadpool, 0, sizeof(tThreadpoolInfo)); 34 threadpool->threadMaxNum = threadMaxNum; 35 threadpool->threadStartStep = threadStartStep; 36 threadpool->taskQueueSize = taskQueueSize; 37 38 /* 分配執行緒儲存資源 */ 39 threadpool->threads = (pthread_t *)calloc(threadMaxNum, sizeof(pthread_t)); 40 if (NULL == threadpool->threads) 41 { 42 THREADPOOL_ERR("malloc threads failed.\r\n"); 43 goto error_exit; 44 } 45 46 /* 分配任務佇列 */ 47 threadpool->taskQueue = (tThreadTaskInfo *)calloc(taskQueueSize, sizeof(tThreadTaskInfo)); 48 if (NULL == threadpool->taskQueue) 49 { 50 THREADPOOL_ERR("malloc task queue failed.\r\n"); 51 goto error_exit; 52 } 53 54 /* 初始化互斥訊號量和同步訊號 */ 55 if (0 != THREADPOOL_LOCK_INIT(threadpool)) 56 { 57 THREADPOOL_ERR("mutex init failed.\r\n"); 58 goto error_exit; 59 } 60 61 if (0 != THREADPOOL_COND_INIT(threadpool)) 62 { 63 THREADPOOL_ERR("cond init failed.\r\n"); 64 goto error_exit; 65 } 66 67 return threadpool; 68 69 error_exit: 70 71 if (threadpool != NULL) 72 { 73 threadpool_free(threadpool); 74 } 75 76 return NULL; 77 }
向執行緒池新增任務
- 檢視等待佇列是否有空閒,如果沒有空閒則返回錯誤;
- 檢視當前有沒有處於pending的執行緒,如果沒有則按照步進啟動新的執行緒,如果已達到最大執行緒數則返回錯誤;
- 將任務新增到佇列中,並喚醒一個執行緒執行任務;
1 /************************************ 2 * 向執行緒池新增任務 3 * 4 * @threadpool -- 執行緒池引用 5 * @taskfunc -- 任務回撥函式 6 * @arg1 -- 任務第一個引數 7 * @arg1 -- 任務第二個引數 8 * 9 * @Return -- 成功: 0 10 * 失敗: -1 11 * **********************************/ 12 int32_t threadpool_addtask( 13 tThreadpoolInfo *threadpool, 14 THREADPOOLTASKFUNC taskfunc, 15 void *arg1, 16 void *arg2) 17 { 18 int32_t ret = 0; 19 20 if ((NULL == threadpool) || (NULL == taskfunc)) 21 { 22 THREADPOOL_ERR("invalid param.\r\n"); 23 return -1; 24 } 25 26 THREADPOOL_LOCK(threadpool); 27 28 do 29 { 30 if (threadpool->isShutdown) 31 { 32 THREADPOOL_ERR("threadpool is shutdown.\r\n"); 33 ret = -1; 34 break; 35 } 36 37 /* 判斷等待執行的任務佇列是否滿 */ 38 if (threadpool->taskPendCnt == threadpool->taskQueueSize) 39 { 40 THREADPOOL_ERR("task queue is full.\r\n"); 41 ret = -1; 42 break; 43 } 44 45 /* 如果pending狀態的執行緒已用完,則啟動新的執行緒 */ 46 if (threadpool->threadPendCnt <= 0) 47 { 48 if (0 != threadpool_start(threadpool)) 49 { 50 ret = -1; 51 break; 52 } 53 } 54 55 /* 將任務放入對尾 */ 56 threadpool->taskQueue[threadpool->taskQueueTail].func = taskfunc; 57 threadpool->taskQueue[threadpool->taskQueueTail].arg1 = arg1; 58 threadpool->taskQueue[threadpool->taskQueueTail].arg2 = arg2; 59 60 threadpool->taskQueueTail = (threadpool->taskQueueTail + 1) % threadpool->taskQueueSize; 61 threadpool->taskPendCnt++; 62 63 /* 喚醒一個執行緒執行任務 */ 64 THREADPOOL_COND_SIGNAL(threadpool); 65 66 } while(0); 67 68 THREADPOOL_UNLOCK(threadpool); 69 return ret; 70 }
執行緒的回撥函式
- 執行緒第一次啟動和被喚醒後檢查佇列中是否有需要執行的任務,如果沒有則繼續等待喚醒;
- 如果有需要執行的任務,則從佇列中取一個任務並執行;
- 如果執行緒池已銷燬,則退出執行緒;
1 /************************************ 2 * 執行緒回撥函式 3 * 等待執行緒池分配任務並執行分配的任務 4 * 5 * @arg -- 執行緒池引用 6 * **********************************/ 7 void* thread_callback(void *arg) 8 { 9 tThreadpoolInfo *threadpool = (tThreadpoolInfo *)arg; 10 tThreadTaskInfo task; 11 12 while (1) 13 { 14 THREADPOOL_LOCK(threadpool); 15 16 /* 等待任務分配的訊號 17 * 如果當前沒有等待執行的任務,並且執行緒池沒有關閉則繼續等待訊號 */ 18 while ((0 == threadpool->taskPendCnt) 19 && (0 == threadpool->isShutdown)) 20 { 21 THREADPOOL_COND_WAIT(threadpool); 22 } 23 24 /* 如果執行緒池已關閉,則退出執行緒 */ 25 if (threadpool->isShutdown) 26 break; 27 28 /* 取任務佇列中當前第一個任務 */ 29 task.func = threadpool->taskQueue[threadpool->taskQueueHead].func; 30 task.arg1 = threadpool->taskQueue[threadpool->taskQueueHead].arg1; 31 task.arg2 = threadpool->taskQueue[threadpool->taskQueueHead].arg2; 32 33 threadpool->taskQueueHead = (threadpool->taskQueueHead + 1) % threadpool->taskQueueSize; 34 threadpool->taskPendCnt--; 35 threadpool->threadPendCnt--; 36 37 THREADPOOL_UNLOCK(threadpool); 38 39 /* 執行任務 */ 40 (*(task.func))(task.arg1, task.arg2); 41 42 /* 任務執行完成後,執行緒進入pending狀態 */ 43 THREADPOOL_LOCK(threadpool); 44 threadpool->threadPendCnt++; 45 THREADPOOL_UNLOCK(threadpool); 46 } 47 48 threadpool->threadStartCnt--; 49 THREADPOOL_UNLOCK(threadpool); 50 51 pthread_exit(NULL); 52 }
執行緒池銷燬
- 銷燬為確保資源釋放,需要喚醒所有執行緒,並等待所有執行緒退出;
1 /************************************ 2 * 刪除執行緒池 3 * 4 * @threadpool -- 執行緒池引用 5 * **********************************/ 6 int32_t threadpool_destroy(tThreadpoolInfo *threadpool) 7 { 8 int32_t ret = 0; 9 int32_t i = 0; 10 11 if (NULL == threadpool) 12 { 13 THREADPOOL_ERR("invalid param.\r\n"); 14 return -1; 15 } 16 17 THREADPOOL_LOCK(threadpool); 18 19 do 20 { 21 if (threadpool->isShutdown) 22 { 23 THREADPOOL_UNLOCK(threadpool); 24 break; 25 } 26 27 threadpool->isShutdown = 1; 28 29 /* 喚醒所有執行緒 */ 30 if (0 != THREADPOOL_COND_BROADCAST(threadpool)) 31 { 32 THREADPOOL_ERR("cond broadcast failed.\r\n"); 33 threadpool->isShutdown = 0; 34 continue; 35 } 36 37 THREADPOOL_UNLOCK(threadpool); 38 39 /* 等待所有程式退出 */ 40 for (i = 0; i < threadpool->threadStartCnt; i++) 41 { 42 pthread_cancel(threadpool->threads[i]); 43 pthread_join(threadpool->threads[i], NULL); 44 } 45 46 }while(0); 47 48 if (0 != ret) 49 { 50 threadpool->isShutdown = 0; 51 return ret; 52 } 53 54 threadpool_free(threadpool); 55 return ret; 56 }
執行緒池測試
- 建立最大執行緒數=256,佇列大小=64,啟動步進=8 的執行緒池;
- 向執行緒池新增1024個任務,如果新增失敗則等待1秒再新增;
- 驗證1024個任務是否均能執行;
1 /*********************************** 2 * Filename : test_main.c 3 * Author : taopeng 4 * *********************************/ 5 6 #include <stdio.h> 7 #include <stdlib.h> 8 #include <string.h> 9 #include <unistd.h> 10 11 #include "threadpool.h" 12 13 void test_task(void *arg) 14 { 15 long id = (long)arg; 16 17 printf("task[%ld] enter\r\n", id); 18 sleep(3); 19 20 return; 21 } 22 23 int32_t main(int32_t argc, char *argv[]) 24 { 25 tThreadpoolInfo *threadpool; 26 long id; 27 28 threadpool = threadpool_create(128, 8, 64); 29 if (NULL == threadpool) 30 return -1; 31 32 for (id = 1; id <= 1024;) 33 { 34 if (0 != threadpool_addtask(threadpool, (THREADPOOLTASKFUNC)test_task, (void *)id, NULL)) 35 { 36 sleep(1); 37 continue; 38 } 39 40 id++; 41 } 42 43 sleep(30); 44 45 threadpool_destroy(threadpool); 46 return 0; 47 }
程式碼例項連結
https://gitee.com/github-18274965/threadpool.git