线程池概述
对于线程池的原理我不做过多讲解,我们采用生产消费模型,将线程池中的线程视为消费者,通过一个缓冲队列,从中循环获取任务并执行,队列中的元素我们设置为一个结构体,包括任务函数以及其所执行需要的参数。同时每一次取出任务、完成任务后都需要检查线程池的状态,做到安全且优雅地退出。
缓冲队列设计
buffer_queue.h
#pragma once
#ifndef _BUFFER_QUEUE_H
#define _BUFFER_QUEUE_H
#include <stdbool.h>
typedef struct buffer_queue
{
void **array;
int head;
int tail;
int capacity;//缓冲队列容量
int size;//缓冲队列大小
} buffer_queue;
struct buffer_queue *buffer_queue_create(int capacity);//创建一个特定容量的缓冲队列
void buffer_queue_insert(struct array_queue *queue, void *elem);//元素插入缓冲队列
void *buffer_queue_fetch(struct array_queue *queue);//从缓冲队列中取出元素
bool buffer_queue_empty(struct array_queue *queue);//缓冲队列是否为空
bool buffer_queue_full(struct array_queue *queue);//缓冲队列是否满载
void buffer_queue_destroy(struct array_queue *queue);//销毁缓冲队列
#endif
#include "buffer_queue.h"
#include <assert.h>
#include <stdlib.h>
#include <stdbool.h>
struct buffer_queue *buffer_queue_create(int capacity)
{
assert(capacity > 0);
struct buffer_queue *queue = (struct buffer_queue *)malloc(sizeof(struct buffer_queue));
assert(queue != NULL);
queue->array = (void **)malloc(sizeof(void *) * capacity);
assert(queue->array != NULL);
queue->capacity = capacity;//初始化队列1容量
queue->size = 0;
return queue;
}
void buffer_queue_insert(struct buffer_queue *queue, void *elem)
{
queue->array[queue->tail] = elem;
queue->tail = (queue->tail + 1) % queue->capacity;
queue->size++;
}
void *buffer_queue_fetch(struct buffer_queue *queue)
{
void *elem = queue->array[queue->head];
queue->head = (queue->head + 1) % queue->capacity;
queue->size--;
return elem;
}
bool buffer_queue_empty(struct buffer_queue *queue)
{
return queue->size == 0 ? true : false;
}
bool array_queue_full(struct array_queue *queue)
{
return queue->size == queue->capacity ? true : false;
}
void array_queue_destroy(array_queue *queue)
{
free(queue->array);
free(queue);
}
这里之所以采用循环数组队列而不采用链式队列主要原因是考虑到后续定义队列元素不需要设置next结点指针。
线程池中工作线程
考虑到后续的功能扩展,这里我们选取多生产者--多消费者模型,同时为了操作缓冲队列的线程安全,我们需要为其设置一个互斥锁。
在该线程池中,工作线程作为消费者,同样为了使线程池以及主线程能够更好地管理工作线程,我们采用第一章中封装好地lio_thread记录每个工作线程的信息,同时,根据工作线程是否在执行任务对其标记为不同的状态,以便管理线程能够做出决策。
线程池管理线程
我们设想这样一个场景,最初我们创建了一定量的线程,如果队列中存在大量的任务,使每个工作线程都处于忙碌状态,很明显需要临时创建更多线程来解决待完成的任务;再设想一个场景,同样我们创建了一定量的线程,如果队列中的任务只需要一两个线程取出完成即可,其他线程往往处于阻塞在互斥锁等待人物的空闲状态,那么最好应该减少线程的数量,降低上下文切换的频率。
因此我们需要设计一个线程池的管理者,用于监测线程池中空闲、忙碌线程的数量,对工作线程做出一定量增减,保证线程池的高性能。
线程池最终设计
typedef void *(thread_fun)(void *);
typedef void(send_fun)(void *);
typedef void *(work_fun)(void *);//任务函数类型
typedef enum
{
BUSY,//执行任务,忙碌
EMPTY,//等待任务,空闲
DEAD,//被管理线程杀死
UNBIRTH,//未被创建
} STATUS;//工作线程状态
typedef struct thread_worker
{
int worker_id;//工作线程数组索引
STATUS worker_status_state;//工作线程状态
struct lio_thread thread;
struct thread_pool *pool_p;//工作线程所属线程池
} thread_worker;
typedef struct thread_regulor
{
struct lio_thread thread;
int regulor_detect_time;//管理者监测线程池间隔
int regulor_tolerate_time;//检测到忙碌或空闲线程过多,再隔一段时间查看
struct thread_pool *pool_p;//管理线程所管理的线程池
} thread_regulor;
typedef struct thread_pool
{
int work_thread_min_num;//设置池所允许的最大线程数量
int work_thread_max_num;//设置池所允许的最小线程数量
int work_thread_exit_num;//池存在线程数量
int work_thread_empty_num;//池中空闲线程数量
int work_thread_busy_num;//池中忙碌线程数量
thread_worker *thread_worker_arry;//工作线程信息记录数组
struct buffer_queue *queue;//缓冲队列
pthread_mutex_t queue_mutex;//缓冲队列互斥锁
pthread_cond_t queue_full_cond;//队列满载条件变量
pthread_cond_t queue_empty_cond;//队列为空条件变量
thread_regulor *regulor_p;//管理线程
} thread_pool;
typedef struct work_unit
{
void *work_fun;//任务函数
void *work_data;//任务函数所需参数
} work_unit;
void send_work_func(thread_pool *pool, void *send_elem);//向线程池中投送任务
void thread_pool_init(thread_pool *pool, int queue_capacity, int work_thread_min_num, int work_thread_max_num);//初始化线程池
void thread_regulor_init(struct thread_regulor *regulor_p, int regulor_detect_time, int regulor_tolerate_time);//设置管理线程
void thread_pool_bind_regulor(thread_pool *pool, thread_regulor *regulor_p);//绑定管理线程与线程池
void thread_pool_run(thread_pool *pool);//运行线程池
void thread_pool_destory(thread_pool *pool);//销毁线程池
void *thread_pool_worker(void *q);//工作线程运行函数
void *thread_pool_regulor(void *q);//管理线程运行函数
#endif
/*向线程池中投送任务*/
void send_work_func(thread_pool *pool, void *send_elem)
{
if (send_elem == NULL)//避免空任务
{
write(STDOUT_FILENO, "unit is NULL!\n", 15);
exit(0);
}
/*如果队列未满载则投放任务*/
pthread_mutex_lock(&pool->queue_mutex);
while (array_queue_full(pool->queue))
{
pthread_cond_wait(&pool->queue_empty_cond, &pool->queue_mutex);
}
array_queue_insert(pool->queue, send_elem);
pthread_cond_signal(&pool->queue_full_cond);
pthread_mutex_unlock(&pool->queue_mutex);
return;
}
/*
初始化线程池
*/
void thread_pool_init(thread_pool *pool, int queue_capacity, int work_thread_min_num, int work_thread_max_num)
{
/*设置缓冲队列容量*/
pool->queue = array_queue_create(queue_capacity);
pool->queue->capacity = queue_capacity;
/*设置线程池允许工作线程最大最小数量*/
pool->work_thread_min_num = work_thread_min_num;
pool->work_thread_max_num = work_thread_max_num;
/*设置未运行前各种工作线程数量*/
pool->work_thread_exit_num = work_thread_min_num;
pool->work_thread_empty_num = work_thread_min_num;
pool->work_thread_busy_num = 0;
/*初始化缓冲队列互斥锁与条件变量*/
pthread_mutex_init(&pool->queue_mutex, NULL);
pthread_cond_init(&pool->queue_empty_cond, NULL);
pthread_cond_init(&pool->queue_full_cond, NULL);
/*设置初始化时工作线程信息*/
pool->thread_worker_arry = (thread_worker *)malloc(work_thread_max_num * sizeof(struct thread_worker));
for (int i = 0; i < pool->work_thread_max_num; i++)
{
pool->thread_worker_arry[i].pool_p = pool;
pool->thread_worker_arry[i].worker_id = i + 1;
lio_thread_set(&(pool->thread_worker_arry[i].thread), thread_pool_worker, pool->thread_worker_arry + i);
/*部分在线程池运行时不会创建*/
if (i >= pool->work_thread_exit_num)
{
pool->thread_worker_arry[i].worker_status_state = UNBIRTH;
}
else
{
pool->thread_worker_arry[i].worker_status_state = EMPTY;
}
}
/*默认未绑定管理线程*/
pool->regulor_p = NULL;
}
/*为线程池绑定一个管理线程*/
void thread_pool_bind_regulor(thread_pool *pool, thread_regulor *regulor_p)
{
if (regulor_p)
{
pool->regulor_p = regulor_p;
regulor_p->pool_p = pool;
lio_thread_set(®ulor_p->thread, thread_pool_regulor, regulor_p);
}
}
/*为管理线程设置监测间隔、容忍间隔*/
void thread_regulor_init(struct thread_regulor *regulor_p, int regulor_detect_time, int regulor_tolerate_time)
{
regulor_p->regulor_detect_time = regulor_detect_time;
regulor_p->regulor_tolerate_time = regulor_tolerate_time;
}
/*开始运行线程池,先创建工作线程,再创建管理线程*/
void thread_pool_run(thread_pool *pool)
{
/*create worker thread*/
for (int i = 0; i < pool->work_thread_exit_num; i++)
{
lio_thread_run(&pool->thread_worker_arry[i].thread);
}
if (pool->regulor_p)
{
lio_thread_run(&pool->regulor_p->thread);
}
}
/*工作线程运行函数*/
void *thread_pool_worker(void *q)
{
void **args = q;
struct lio_thread *lio_thread_p = args[0];
struct thread_worker *w = args[1];
struct thread_pool *p = w->pool_p;
/*循环等待任务并执行*/
for (;;)
{
/*receive work unit if queue not empty*/
pthread_mutex_lock(&p->queue_mutex);
while (array_queue_empty(p->queue) && lio_thread_p->state == LIO_THREAD_RUN)
{
pthread_cond_wait(&p->queue_full_cond, &p->queue_mutex);
}
if (lio_thread_p->state == LIO_THREAD_QUIT)//线程池关闭子线程退出
{
pthread_mutex_unlock(&p->queue_mutex);
return NULL;
}
/*从缓冲队列中取出一个任务*/
struct work_unit *unit = array_queue_fetch(p->queue);
pthread_cond_signal(&p->queue_empty_cond);
/*取出任务后将线程状态设置为忙碌*/
w->worker_status_state = BUSY;
p->work_thread_busy_num++;
p->work_thread_empty_num--;
pthread_mutex_unlock(&p->queue_mutex);
/*执行任务后释放申请内存*/
work_fun *work = unit->work_fun;
work(unit->work_data);
cache_pool_recycle((void *)unit);
/*执行任务后改变工作线程状态*/
pthread_mutex_lock(&p->queue_mutex);
w->worker_status_state = EMPTY;
p->work_thread_busy_num--;
p->work_thread_empty_num++;
pthread_mutex_unlock((&p->queue_mutex));
}
}
/*销毁线程池,首先杀死管理线程,再杀死工作线程*/
void thread_pool_destory(thread_pool *p)
{
/*杀死管理线程*/
if (p->regulor_p)
{
pthread_mutex_lock(&p->queue_mutex);
lio_thread_exit(&p->regulor_p->thread);
pthread_mutex_unlock(&p->queue_mutex);
pthread_join(p->regulor_p->thread.thread_id, NULL);
write(STDOUT_FILENO, "regulor exit!\n", 15);
}
/*等待缓冲队列中所有任务被做完*/
pthread_mutex_lock(&p->queue_mutex);
while (p->queue->size != 0 && p->work_thread_busy_num != 0)
{
pthread_cond_signal(&p->queue_full_cond);
pthread_cond_wait(&p->queue_empty_cond, &p->queue_mutex);
}
write(STDOUT_FILENO, "reset clear!\n", 14);
/*杀死所有工作线程*/
for (int i = 0; i < p->work_thread_max_num; i++)
{
if (p->thread_worker_arry[i].worker_status_state == EMPTY)
{
lio_thread_exit(&(p->thread_worker_arry[i].thread));
}
}
pthread_cond_broadcast(&p->queue_full_cond);
pthread_mutex_unlock(&p->queue_mutex);
for (int i = 0; i < p->work_thread_max_num; i++)
{
if (p->thread_worker_arry[i].worker_status_state == EMPTY)
{
pthread_join(p->thread_worker_arry[i].thread.thread_id, NULL);
}
}
/*销毁条件变量与锁*/
pthread_mutex_destroy(&p->queue_mutex);
pthread_cond_destroy(&p->queue_empty_cond);
pthread_cond_destroy(&p->queue_full_cond);
array_queue_destroy(p->queue);
free(p->thread_worker_arry);
}
/*管理线程运行函数*/
void *thread_pool_regulor(void *q)
{
void **args = q;
struct lio_thread *lio_thread_p = args[0];
struct thread_regulor *r = args[1];
struct thread_pool *p = r->pool_p;
while (true)
{
sleep(r->regulor_detect_time);
if (lio_thread_p->state == LIO_THREAD_QUIT)
{
return NULL;
}
pthread_mutex_lock(&p->queue_mutex);
if (lio_thread_p->state == LIO_THREAD_QUIT)
{
return NULL;
}
/*监测是否所有工作线程都处于忙碌状态*/
if (p->work_thread_exit_num == p->work_thread_busy_num)
{
pthread_mutex_unlock(&p->queue_mutex);
sleep(r->regulor_tolerate_time);
pthread_mutex_lock(&p->queue_mutex);
if (p->work_thread_exit_num == p->work_thread_busy_num && p->work_thread_exit_num < p->work_thread_max_num) // new a thread worker if rest
{
for (int i = 0; i < p->work_thread_max_num; i++)
{
/*创建一个新线程*/
if (p->thread_worker_arry[i].worker_status_state == DEAD || p->thread_worker_arry[i].worker_status_state == UNBIRTH)
{
p->thread_worker_arry[i].worker_status_state = EMPTY;
p->work_thread_exit_num++;
p->work_thread_empty_num++;
lio_thread_set(&p->thread_worker_arry[i].thread, thread_pool_worker, p->thread_worker_arry + i);
lio_thread_run(&p->thread_worker_arry[i].thread);
break;
}
}
}
pthread_mutex_unlock(&p->queue_mutex);
}
/*如果空闲线程大于线程池所允许的最小存在工作线程数*/
else if (p->work_thread_empty_num > p->work_thread_min_num)
{
pthread_mutex_unlock(&p->queue_mutex);
sleep(r->regulor_tolerate_time);
pthread_mutex_lock(&p->queue_mutex);
if (p->work_thread_empty_num > p->work_thread_min_num)
{
/*杀死一个空闲的工作线程*/
for (int i = 0; i < p->work_thread_max_num; i++)
{
if (p->thread_worker_arry[i].worker_status_state == EMPTY)
{
lio_thread_exit(&p->thread_worker_arry[i].thread);
p->thread_worker_arry[i].worker_status_state = DEAD;
p->work_thread_exit_num--;
p->work_thread_empty_num--;
pthread_mutex_unlock(&p->queue_mutex);
if (pthread_join(p->thread_worker_arry[i].thread.thread_id, NULL) == -1)
{
perror("thread join");
};
break;
}
}
}
else
{
pthread_mutex_unlock(&p->queue_mutex);
}
}
else
{
pthread_mutex_unlock(&p->queue_mutex);
}
}
return NULL;
}