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 著作权归作者所有。请勿转载和采集!