结构体定义
首先定义存储任务信息的结构体,我们可以将其抽象为函数指针和参数,如下所示
typedef struct
{
void *(*function)(void *);// 函数指针
void *arg;// 函数参数
}THREAD_POOL_TASK;
接下来定义线程池的结构体,包含线程池的资源和状态等属性,如下所示
typedef struct
{
int thread_num;// 工作线程数量
int task_queue_max_size;// 任务队列的容量
int task_queue_num;// 加入的任务数量
int task_queue_front;// 将要保存任务信息的队列索引
int task_queue_rear;// 将要取出任务信息的队列索引
pthread_mutex_t lock;// 线程池状态变量操作的互斥锁
pthread_cond_t task_queue_not_full;// 任务队列有空闲空间的信号
pthread_cond_t task_queue_not_empty;// 任务队列中有待处理任务的信号
pthread_t *pt_threads;// 工作线程数组指针
THREAD_POOL_TASK *pt_task_queue;// 任务队列,环形缓冲区形式
bool shutdown;// 销毁标志
}THREAD_POOL_ST;
接口定义
1)线程池初始化
初始化接口定义如下,入参包括线程池句柄指针,工作线程数量,任务队列容量;这里为了避免泄漏具体实现,将线程池句柄的类型设为了void*型。
bool ThreadPool_Init(void **ppv_handle, int thread_num, int task_max_size)
{
bool ret = true;
// 判断入参合法性
if((ppv_handle == NULL) || (thread_num <= 0) || (task_max_size <= 0))
{
printf("Error: input param is invalid\n");
ret = false;
return ret;
}
// 申请线程池资源并做初始化
THREAD_POOL_ST *pt_pool = (THREAD_POOL_ST *)malloc(sizeof(THREAD_POOL_ST));
pt_pool->shutdown = false;
pt_pool->thread_num = thread_num;
pt_pool->pt_threads = NULL;
if(pt_pool->thread_num > 0)
{
pt_pool->pt_threads = (pthread_t *)malloc(sizeof(pthread_t) * pt_pool->thread_num);
memset(pt_pool->pt_threads, 0, sizeof(pthread_t) * pt_pool->thread_num);
}
pt_pool->task_queue_max_size = task_max_size;
pt_pool->pt_task_queue = (THREAD_POOL_TASK*)malloc(sizeof(THREAD_POOL_TASK) * pt_pool->task_queue_max_size);
pt_pool->task_queue_num = 0;
pt_pool->task_queue_front = 0;
pt_pool->task_queue_rear = 0;
pthread_mutex_init(&(pt_pool->lock), NULL);
pthread_cond_init(&(pt_pool->task_queue_not_empty), NULL);
pthread_cond_init(&(pt_pool->task_queue_not_full), NULL);
for(int n = 0; n < pt_pool->thread_num; n++)
{
pthread_create(&(pt_pool->pt_threads[n]), NULL, ThreadPool_WorkThread, (void*)pt_pool);
}
*ppv_handle = pt_pool;
return ret;
}
2)工作线程
工作线程负责竞争获取任务队列中的任务并执行,如果竞争不到任务则进入休眠。
static void ThreadPool_WorkThread(void *arg)
{
// arg为线程池句柄
THREAD_POOL_ST *pt_pool = (THREAD_POOL_ST *)arg;
THREAD_POOL_TASK task;
while(1)
{
// 判断线程池是否准备销毁,如果不是是否有待处理的任务;考虑误唤醒以及多线程竞争
// 的情况这里使用的是while()循环判断
pthread_mutex_lock(&(pt_pool->lock));
while((pt_pool->task_queue_num == 0) && (!pt_pool->shutdown))
{
// 等待任务队列不为空的信号
pthread_cond_wait(&(pt_pool->task_queue_not_empty), &(pt_pool->lock));
}
// 如果线程池准备销毁,则释放资源并退出
if(pt_pool->shutdown)
{
pthread_mutex_unlock(&(pt_pool->lock));
pthread_exit(NULL);
}
// 从任务队列取任务
task.function = pt_pool->pt_task_queue[pt_pool->task_queue_rear].function;
task.arg = pt_pool->pt_task_queue[pt_pool->task_queue_rear].arg;
pt_pool->task_queue_rear = (pt_pool->task_queue_rear + 1) % pt_pool->task_queue_max_size;
pt_pool->task_queue_num--;
// 发送任务队列有了空闲空间的信号
pthread_cond_broadcast(&(pt_pool->task_queue_not_full));
pthread_mutex_unlock(&(pt_pool->lock));
// 执行任务
(*(task.function))(task.arg);
}
pthread_exit(NULL);
}
3)添加任务
将任务信息添加到线程池的任务队列中,如果任务队列无空间则会阻塞等待。
bool ThreadPool_AddTask(void *pv_handle, void *(*function)(void *arg), void *arg)
{
bool ret = true;
// 判断参数合法性
if((NULL == pv_handle) || (NULL == function))
{
printf("Error: input param is invalid\n");
ret = false;
return ret;
}
THREAD_POOL_ST *pt_pool = (THREAD_POOL_ST *)pv_handle;
pthread_mutex_lock(&(pt_pool->lock));
// 确保线程池中的任务队列有空间
while((pt_pool->task_queue_num == pt_pool->task_queue_max_size) && (!pt_pool->shutdown))
{
pthread_cond_wait(&(pt_pool->task_queue_not_full), &(pt_pool->lock));
}
// 线程池需要销毁,释放资源后退出
if(pt_pool->shutdown)
{
pthread_mutex_unlock(&(pt_pool->lock));
ret = false;
return ret;
}
// 向任务队列中添加任务
pt_pool->pt_task_queue[pt_pool->task_queue_front].function = function;
pt_pool->pt_task_queue[pt_pool->task_queue_front].arg = arg;
pt_pool->task_queue_front = (pt_pool->task_queue_front + 1) % pt_pool->task_queue_max_size;
pt_pool->task_queue_num++;
// 发送任务队列不为空的信号
pthread_cond_signal(&(pt_pool->task_queue_not_empty));
pthread_mutex_unlock(&(pt_pool->lock));
return ret;
}
4)销毁线程池
销毁线程池时会确保线程池中任务队列的所有任务都执行完毕,再释放线程池资源。
bool ThreadPool_Destroy(void* pv_handle)
{
// 判断入参合法性
bool ret = true;
if(NULL == pv_handle)
{
printf("Error: input param is invalid\n");
ret = false;
return ret;
}
THREAD_POOL_ST *pt_pool = (THREAD_POOL_ST *)pv_handle;
// 确保所有任务执行完毕
pthread_mutex_lock(&(pt_pool->lock));
while(pt_pool->task_queue_num > 0)
{
pthread_mutex_unlock(&(pt_pool->lock));
#ifdef _WIN32
Sleep(10);
#else
usleep(10000);
#endif
pthread_mutex_lock(&(pt_pool->lock));
}
// 设置线程池销毁标志,并唤醒所有工作线程使其退出
pt_pool->shutdown = true;
pthread_mutex_unlock(&(pt_pool->lock));
for(int n = 0; n < pt_pool->thread_num; n++)
{
pthread_cond_broadcast(&(pt_pool->task_queue_not_empty));
}
for(int n = 0; n < pt_pool->thread_num; n++)
{
pthread_join(pt_pool->pt_threads[n], NULL);
}
// 释放线程池资源
if(pt_pool->pt_task_queue)
{
free(pt_pool->pt_task_queue);
pt_pool->pt_task_queue = NULL;
}
if(pt_pool->pt_threads)
{
free(pt_pool->pt_threads);
pt_pool->pt_threads = NULL;
}
pthread_mutex_destroy(&(pt_pool->lock));
pthread_cond_destroy(&(pt_pool->task_queue_not_empty));
pthread_cond_destroy(&(pt_pool->task_queue_not_full));
free(pt_pool);
return ret;
}