C语言实现线程池 - 详解及代码示例
C语言线程池实现详解
线程池是一种用于管理和复用线程的机制,它可以提高程序的并发性能并降低资源消耗。本文将介绍如何使用C语言实现一个简单的线程池,并提供详细的代码解释。
1. 线程池的数据结构
首先,我们需要定义两个结构体:Task 和 ThreadPool。
#define POOL_NUM 10
typedef struct Task
{
    void* (*func)(void* arg);  // 任务函数指针
    void *arg;                 // 任务函数参数
    struct Task *ptNext;        // 指向下一个任务的指针
}tTask_T,*PTTask;
typedef struct ThreadPool
{
    pthread_mutex_t taskMutex;    // 保护任务队列的互斥锁
    pthread_cond_t  newtask;     // 通知工作线程有新任务的条件变量
    pthread_t       tid[POOL_NUM]; // 工作线程ID数组
    PTTask          queue_head;    // 任务队列头指针
    int             busywork;      // 忙碌的工作线程数量
}tThreadPool_T,*PTThreadPool;
Task结构体表示一个任务,包含了任务函数指针、参数和指向下一个任务的指针。ThreadPool结构体表示线程池,包含了互斥锁、条件变量、工作线程ID数组、任务队列头指针以及忙碌的工作线程数量。
2. 线程池的初始化
PTThreadPool ptThreadPool;
void poolInit()
{
    ptThreadPool = malloc(sizeof(tThreadPool_T));
    pthread_mutex_init(&ptThreadPool->taskMutex,NULL);
    pthread_cond_init(&ptThreadPool->newtask,NULL);
    ptThreadPool->queue_head = NULL;
    ptThreadPool->busywork   = 0;
    for(int i = 0;i < POOL_NUM;i++)
    {
        pthread_create(&ptThreadPool->tid[i],NULL,workThread,NULL);
    }
}
poolInit 函数用于初始化线程池,它会分配线程池所需的内存,初始化互斥锁和条件变量,并将任务队列置为空。然后,它会创建指定数量的工作线程,并将其设置为分离状态。
3. 工作线程函数
void *workThread(void *arg)
{
    while (1)
    {
        pthread_mutex_lock(&ptThreadPool->taskMutex);
        pthread_cond_wait(&ptThreadPool->newtask,&ptThreadPool->taskMutex);
        PTTask ptTask = ptThreadPool->queue_head;
        ptThreadPool->queue_head = ptThreadPool->queue_head->ptNext;
        pthread_mutex_unlock(&ptThreadPool->taskMutex);
        ptTask->func(ptTask->arg);  // 执行任务
        ptThreadPool->busywork--;
        
    }
    
}
workThread 函数是工作线程的入口函数。每个工作线程都会循环执行以下操作:
- 获取互斥锁。
 - 等待条件变量,直到有新任务到来。
 - 从任务队列中取出一个任务。
 - 释放互斥锁。
 - 执行任务。
 - 将忙碌的工作线程数量减1。
 
4. 添加任务到线程池
void poolAddTask(int arg)
{
    PTTask ptNewTask;
    pthread_mutex_lock(&ptThreadPool->taskMutex);
    while (ptThreadPool->busywork>=POOL_NUM)
    {
        pthread_mutex_unlock(&ptThreadPool->taskMutex);
        usleep(10000);  // 等待一段时间
        pthread_mutex_lock(&ptThreadPool->taskMutex);
    }
    pthread_mutex_unlock(&ptThreadPool->taskMutex);
    ptNewTask = malloc(sizeof(tTask_T));
    ptNewTask->func = realTask;
    ptNewTask->arg  = &arg;
    pthread_mutex_lock(&ptThreadPool->taskMutex);
    PTTask ptmember = ptThreadPool->queue_head;
    if(ptmember == NULL)  // 任务队列为空
    {
        ptThreadPool->queue_head = ptNewTask;
    }
    else  // 将任务添加到队列尾部
    {
        while (ptmember->ptNext != NULL)
        {
            ptmember = ptmember->ptNext;
        }
        ptmember->ptNext = ptNewTask;
    }
    ptThreadPool->busywork++;
    pthread_cond_signal(&ptThreadPool->newtask);  // 通知工作线程
    pthread_mutex_unlock(&ptThreadPool->taskMutex);
}
poolAddTask 函数用于向线程池中添加任务。它会先检查线程池是否已满,如果已满则等待一段时间。然后,它会创建一个新任务,并将其添加到任务队列的尾部。最后,它会通过条件变量通知工作线程有新任务可执行。
5. 销毁线程池
void poolDestory()
{
    PTTask ptHead;
    while (ptThreadPool->queue_head != NULL)
    {
        ptHead = ptThreadPool->queue_head;
        free(ptHead);
        ptThreadPool->queue_head = ptThreadPool->queue_head->ptNext;
    }
    pthread_mutex_destroy(&ptThreadPool->taskMutex);
    pthread_cond_destroy(&ptThreadPool->newtask);
    free(ptThreadPool);
    
}
poolDestroy 函数用于销毁线程池,它会释放任务队列中的所有任务,并销毁互斥锁和条件变量。
6. 示例程序
void* realTask(void *arg)
{
    printf('finish work %d\n',*((int*)arg));
}
int main()
{
    poolInit();
    sleep(1);
    for(int i = 0; i < 20; i++)
    {
        poolAddTask(i);
    }
    sleep(5);  // 等待任务执行完毕
    poolDestory();
    return 0;
}
在 main 函数中,我们首先调用 poolInit 函数初始化线程池。然后,我们使用一个循环向线程池中添加20个任务。最后,我们等待一段时间以确保所有任务都已执行完毕,然后调用 poolDestroy 函数销毁线程池。
7. 总结
本文介绍了如何使用C语言实现一个简单的线程池。线程池可以提高程序的并发性能,但需要注意线程安全和资源管理等问题。在实际应用中,您可能需要根据具体需求对线程池进行适当的修改和扩展。
原文地址: https://www.cveoy.top/t/topic/bwjJ 著作权归作者所有。请勿转载和采集!