基础知识传送门:
小抄一波概念:
啥是线程池?
线程池就是首先创建一些线程,它们的集合称为线程池。使用线程池可以很好地提高性能,线程池在系统启动时即创建大量空闲的线程,程序将一个任务传给线程池,线程池就会启动一条线程来执行这个任务,执行结束以后,该线程并不会死亡,而是再次返回线程池中成为空闲状态,等待执行下一个任务。
来欣赏张图[截取网上]
再看下线程池的常规结构,也成劝退体。很多人一看这个东西头就大,内容太多,直接不看了。下面来挑战下吧。
typedef struct threadpool_t {
pthread_mutex_t lock; /* 用于锁住本结构体 ,和条件变量一起使用 */
pthread_mutex_t thread_counter; /* 记录忙状态线程个数的锁 -- busy_thr_num */
pthread_cond_t queue_not_full; /* 当任务队列满时,添加任务的线程阻塞,等待此条件变量 */
pthread_cond_t queue_not_empty; /* 任务队列里不为空时,通知线程池中等待任务的线程 */
pthread_t *threads; /* 存放线程池中每个线程的tid。数组 */
pthread_t adjust_tid; /* 存管理线程tid */
threadpool_task_t *task_queue; /* 任务队列 */
int min_thr_num; /* 线程池最小线程数 */
int max_thr_num; /* 线程池最大线程数 */
int live_thr_num; /* 当前存活线程个数 */
int busy_thr_num; /* 忙状态线程个数 */
int wait_exit_thr_num; /* 要销毁的线程个数 */
int queue_front; /* task_queue队头下标 */
int queue_rear; /* task_queue队尾下标 */
int queue_size; /* task_queue队中实际任务数 */
int queue_max_size; /* task_queue队列可容纳任务数上限 */
int shutdown; /* 标志位,线程池使用状态,true或false */
} threadpool_t;
对常规代码我进行了一些小优化修改,不过还有点问题待解决。
已修改:
删除 pthread_cond_broadcast 冗余广播
/*通知所有的空闲线程, 不需要循环广播*
/pthread_cond_broadcast(&(pool->queue_not_empty));
修改线程池的释放的逻辑,无需死等或者任务未完成却 exit 掉
/* 释放线程池 */
if (busyNum == 0 && taskNum == 0)
{
threadpool_destroy(thp);
return 0;
}
待解决:
threadpool_add, 在堵塞阶段且都为耗时任务时, 会极低概率的情况出现 arg 还在使用却 free 掉了的安全问题(这一块的逻辑不太好改,暂时没更好的思路)
/* 清空 工作线程 调用的回调函数 的参数arg */
if (pool->task_queue[pool->queue_rear].arg != NULL)
{
free(pool->task_queue[pool->queue_rear].arg);//这里调用free不安全,因为有可能线程执行函数中, 忙符合时低概率
pool->task_queue[pool->queue_rear].arg = NULL;
}
完整的功能实现总共快 400 行了,就不贴在这里了,给你们一个 gayhub 连接,也看的舒服。
https://zhuanlan.zhihu.com/p/141615042