簡易執行緒池實現

lethe1203發表於2024-04-04

程式中,頻繁地呼叫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;
}

佇列相當於是一個共享變數,這些子執行緒會不斷地取任務去執行,有任務出隊有任務入隊,同一個時刻只能有一個任務進行操作。條件變數用來控制執行緒的睡眠、啟動和銷燬時機。

執行結果:

只提供大體思路,上面的程式暫時存在一些小問題,待解決

相關文章