程式中,頻繁地呼叫pthread_create函式建立執行緒,非常浪費時間,尤其在伺服器端的時候,多執行緒的使用情況下頻繁啟用執行緒和釋放執行緒資源,這樣也會影響程式的執行效率。
可以先在伺服器空閒的時候先建立多個執行緒,我們先線上程函式里面使用pthread_cond_wait將其休眠,有任務過來的時候使用pthread_cond_signal啟用執行緒。
在多執行緒(2)-執行緒同步條件變數 - lethe1203 - 部落格園 (cnblogs.com)一節demo中,使用條件變數和Mutex實現生產者和消費者模型,有一點很關鍵:
pthread_cond_wait函式里面發生了什麼? --釋放了互斥鎖,pthread_cond_wait函式里面第二個引數是互斥鎖
生產者執行緒如果沒有通知消費者執行緒,消費者執行緒就無法把鎖unlock,這樣也保證了生產者執行緒對臨界區的唯一性訪問
這也就是下面簡易執行緒池的核心所在:
threadpool.h
#include <stdio.h> #include <pthread.h> #include <stdlib.h> #include <unistd.h> // queue node typedef struct Task { void (*function)(void *arg); void *arg; struct Task *next; } Task; typedef struct ThreadPool { Task *queueFront; Task *queueRear; int num; pthread_t *threadID; pthread_mutex_t mutex; pthread_cond_t cond; // shutdown pool int shutdown; } ThreadPool;
threadpool.c
#include "threadpool.h" void *worker(void *arg) { ThreadPool *pool = (ThreadPool *)arg; while (1) { pthread_mutex_lock(&pool->mutex); // 如果任務佇列為空,且執行緒池沒有被關閉,執行緒睡眠 while (pool->queueFront == pool->queueRear && pool->shutdown == 0) { // 阻塞等待任務 pthread_cond_wait(&pool->cond, &pool->mutex); } if (pool->shutdown == 1) { printf("--------------------debug8\n"); pthread_mutex_unlock(&pool->mutex); printf("thread shutdown thread %ld exit ...\n", pthread_self()); pthread_exit((void *)0); } Task task; Task *t = pool->queueFront->next; task.function = t->function; task.arg = t->arg; pool->queueFront->next = t->next; free(t); if (pool->queueFront->next == NULL) { pool->queueRear = pool->queueFront; } pthread_mutex_unlock(&pool->mutex); printf("thread %ld start working ...\n", pthread_self()); task.function(task.arg); // 函式指標呼叫函式 printf("thread %ld end working ...\n", pthread_self()); } } ThreadPool *create_thread_pool(int num) { // printf("--------------------debug1"); ThreadPool *pool = (ThreadPool *)malloc(sizeof(ThreadPool)); if (pool == NULL) { printf("malloc threadpool memory failed\n"); return NULL; } // printf("--------------------debug2"); pool->queueFront = (Task *)malloc(sizeof(Task)); if (pool->queueFront == NULL) { fprintf(stderr, "malloc Task failed\n"); goto pfree; return NULL; } // printf("--------------------debug3"); // pool->queueRear = pool->queueFront; pool->queueFront->next = NULL; pool->queueRear = pool->queueFront; pool->num = num; // printf("--------------------debug4"); // init threadid pool->threadID = (pthread_t *)malloc(sizeof(pthread_t) * num); if (pool->threadID == NULL) { fprintf(stderr, "malloc threadID failed\n"); goto qfree; return NULL; } // printf("--------------------debug5"); for (int i = 0; i < num; i++) { if (pthread_create(&pool->threadID[i], NULL, worker, pool) != 0) { fprintf(stderr, "pthread_create threadID failed\n"); goto tfree; return NULL; } pthread_detach(pool->threadID[i]); // pthread detach } // printf("--------------------debug6"); pthread_mutex_init(&pool->mutex, NULL); pthread_cond_init(&pool->cond, NULL); pool->shutdown = 0; // 0: running 1: shutsown return pool; pfree: free(pool); return 0; qfree: free(pool->queueFront); free(pool); return 0; tfree: free(pool->threadID); free(pool->queueFront); free(pool); return 0; } void taskfunction(void *arg) { int num = *(int *)arg; printf("thread %ld is working num = %d ...\n", pthread_self(), num); sleep(1); free(arg); // return 0; } void thread_pool_add(ThreadPool *pool, void (*func)(void *), void *arg) { pthread_mutex_lock(&pool->mutex); Task *t = (Task *)malloc(sizeof(Task)); if (t == NULL) { fprintf(stderr, "malloc Task failure\n"); return; } // 一個任務包含三個部分,執行緒函式,引數,指向下一個任務節點的指標 t->function = func; t->arg = arg; t->next = NULL; pool->queueRear->next = t; pool->queueRear = t; pthread_mutex_unlock(&pool->mutex); pthread_cond_signal(&pool->cond); } void thread_pool_destroy(ThreadPool *pool) { // 關閉執行緒池 pool->shutdown = 1; printf("--------------------debug7\n"); // 喚醒所有執行緒 for (int i = 0; i < pool->num; i++) { pthread_cond_signal(&pool->cond); } // 釋放執行緒號 if (pool->threadID) { free(pool->threadID); } // 釋放任務佇列 while (pool->queueFront->next) { Task *t = pool->queueFront->next; pool->queueFront->next = t->next; free(t); } free(pool->queueFront); // 釋放互斥鎖和條件變數 pthread_mutex_destroy(&pool->mutex); pthread_cond_destroy(&pool->cond); // 釋放執行緒池 free(pool); } int main() { ThreadPool *pool = create_thread_pool(10); if (pool == NULL) { printf("create thread pool failed\n"); return -1; } printf("threadPool create success.\n"); sleep(1); for (int i = 0; i < 5; i++) { int *n = (int *)malloc(sizeof(int)); *n = i; // 將任務新增到任務佇列,taskfunction任務函式 thread_pool_add(pool, taskfunction, n); } sleep(6); thread_pool_destroy(pool); return 0; }
佇列相當於是一個共享變數,這些子執行緒會不斷地取任務去執行,有任務出隊有任務入隊,同一個時刻只能有一個任務進行操作。條件變數用來控制執行緒的睡眠、啟動和銷燬時機。
執行結果:
只提供大體思路,上面的程式暫時存在一些小問題,待解決