概要
- 线程池的好处
- 线程池的组成
- 线程池中每个线程是如何实现同步的
- 任务放置在任务队列是如实现的
线程池的好处
当只使用1种线程模式,线程过多会带来调度的开销(主要为创建线程与销毁线程以及CPU时间轮转切换的开销)
,进而导致了缓存的局部性和整体性。而线程池维护者多个线程,等待监督管理者分配可执行的任务,管理者只需要设置其回调函数就可以让其自动执行。这样避免了在短时间内创建和销毁线程的代价。线程池不仅能够让内核充分的利用还能防止过度的调用。在线程池中设置线程的数量是由可用的并发处理器,处理内核,内存,网络socket等数量。
线程池的组成
-
线程池管理器,包括如下
1.互斥量
2. 任务队列
3. 环境变量
4. 线程数量
5. 最大任务队列
struct thread_pool_s {
pthread_mutex_t mtx;
thread_pool_queue_t queue;
int_t waiting;
pthread_cond_t cond;
char *name;
uint_t threads;
int_t max_queue;
};
线程池管理器如图
-
任务节点
1.指向下一个任务节点
2. 当前任务标号
3. 任务的上下文
4. 任务的处理函数
struct thread_task_s {
thread_task_t *next;
uint_t id;
void *ctx;
void (*handler)(void *data);
};
- 线程池任务队列
1.永远指向任务队列的队头
2. 永远指向任务队列的队尾
typedef struct {
thread_task_t *first;
thread_task_t **last;
} thread_pool_queue_t;
任务队列如图
线程池中每个线程是如何实现同步的/任务放置
实现线程同步的方式有许多比如
- 互斥量pthread_mutex_
- 读写锁pthread_rwlock_
- 条件变量pthread_cond_
- 信号量sem_
本文这里使用pthread_mutex
thread_mutex_lock(&tp->mtx)
每个线程应该随时监听任务队列是否有任务(线程池是一种争夺式的工作模式),也就是监管者在放置任务到任务队列的时候应该通知线程池里面的线程。
thread_cond_signal(&tp->cond)
具体代码如下
int_t
thread_task_post(thread_pool_t *tp, thread_task_t *task)
{
if (thread_mutex_lock(&tp->mtx) != OK) {
return ERROR;
}
if (tp->waiting >= tp->max_queue) {
(void) thread_mutex_unlock(&tp->mtx);
fprintf(stderr,"thread pool \"%s\" queue overflow: %ld tasks waiting\n",
tp->name, tp->waiting);
return ERROR;
}
//task->event.active = 1;
task->id = thread_pool_task_id++;
task->next = NULL;
if (thread_cond_signal(&tp->cond) != OK) {
(void) thread_mutex_unlock(&tp->mtx);
return ERROR;
}
*tp->queue.last = task; // 尾插法,插入最后一个节点的后面
tp->queue.last = &task->next; //last始终指向任务链表的最后一个节点
tp->waiting++;
(void) thread_mutex_unlock(&tp->mtx);
if(debug)fprintf(stderr,"task #%lu added to thread pool \"%s\"\n",
task->id, tp->name);
return OK;
}
当任务队列没有任务需要做时,此时应该让出cpu,同时释放互斥量。
thread_cond_wait(&tp->cond, &tp->mtx)
具体代码如下
thread_pool_t *tp = data;
int err;
thread_task_t *task;
if(debug)fprintf(stderr,"thread in pool \"%s\" started\n", tp->name);
for ( ;; ) {
if (thread_mutex_lock(&tp->mtx) != OK) {
return NULL;
}
tp->waiting--;
/*
阻塞等待条件变量cond
释放已掌握的互斥锁(解锁互斥量)相当于ptherad_mutex_unlock(&mutex);
当被唤醒,pthread_cond_wait函数返回时,解除阻塞并重新申请获取互斥锁pthread_mutex_lock(&mutex);
*/
while (tp->queue.first == NULL) { //判断任务链表中是否有任务
if (thread_cond_wait(&tp->cond, &tp->mtx)
!= OK)
{
(void) thread_mutex_unlock(&tp->mtx);
return NULL;
}
}
task = tp->queue.first;
tp->queue.first = task->next;
if (tp->queue.first == NULL) {
tp->queue.last = &tp->queue.first;
}
if (thread_mutex_unlock(&tp->mtx) != OK) {
return NULL;
}
if(debug) fprintf(stderr,"run task #%lu in thread pool \"%s\"\n",
task->id, tp->name);
task->handler(task->ctx);
if(debug) fprintf(stderr,"complete task #%lu in thread pool \"%s\"\n",task->id, tp->name);
task->next = NULL;