线程池是一种池化的技术,类似的还有数据库连接池、HTTP 连接池等等.
池化的思想主要是为了减少每次获取和结束资源的消耗,提高对资源的利用率.
线程池的原理类似于生产者和消费者模型,线程池中的线程是消费者,任务队列中的任务则是生产者生产的消费品,当任务队列存在多个任务时,便会由调度器依次将任务派发给现有的线程执行.
实现一个线程池主要有三大部分:
头文件
#include
#include
#include
#include
定义结构体
我们首先定义以下三个结构体:任务节点、员工节点、管理者节点(线程池).
// 任务结构体
struct nTask {
void (*task_func)(struct nTask *task); // 存储待执行的任务
void *user_data; // 任务参数
struct nManager *pool;
struct nTask *prev;
struct nTask *next;
};
// 执行结构体
struct nWorker {
pthread_t threadid;
int terminate;
struct nManager *pool;
struct nWorker *prev;
struct nWorker *next;
};
// 管理组件
typedef struct nManager {
struct nTask *tasks;
struct nWorker *workers;
pthread_mutex_t mutex;
pthread_cond_t cond; // 条件变量
} ThreadPool;
操作队列的公共方法
这里要注意,因为线程池结构体字段中的任务队列和执行者队列都是指针类型,所以下列函数传入的参数必须是二级指针(原来写的是一级指针,找了很久的错误).
为了可扩展性,这里建议写成泛型.
void TASK_INSERT(struct nTask **item, struct nTask **tasks) {
if (*tasks == NULL) {
*tasks = *item;
(*tasks)->next = (*tasks)->prev = NULL;
} else {
(*item)->prev = NULL;
(*item)->next = *tasks;
(*tasks)->prev = *item;
*tasks = *item;
}
}
void TASK_REMOVE(struct nTask **item, struct nTask **tasks) {
if ((*tasks)->next == NULL) {
*item = *tasks;
*tasks = NULL;
} else {
*item = *tasks;
*tasks = (*tasks)->next;
(*tasks)->prev = NULL;
}
}
void WORKER_INSERT(struct nWorker **item, struct nWorker **workers) {
if (*workers == NULL) {
*workers = *item;
(*workers)->next = (*workers)->prev = NULL;
} else {
(*item)->prev = NULL;
(*item)->next = *workers;
(*workers)->prev = *item;
*workers = *item;
}
}
创建并初始化线程池
int nThreadPoolCreate(ThreadPool *pool, int numWorker) {
if (pool == NULL) return -1;
if (numWorker < 1) numWorker = 1;
// 先把pool初始化为空
memset(pool, 0, sizeof(ThreadPool));
// 初始化条件变量
pthread_cond_init(&pool->cond, NULL);
// pthread_cond_t blank_cond = PTHREAD_COND_INITIALIZER;
// memcpy(&pool->cond, &blank_cond, sizeof(pthread_cond_t));
// 初始化互斥锁
pthread_mutex_init(&pool->mutex, NULL);
// pthread_mutex_t blank_mutex = PTHREAD_MUTEX_INITIALIZER;
// memcpy(&pool->mutex, &blank_mutex, sizeof(pthread_mutex_t));
printf("nThreadPoolCreate\n");
int i = 0;
for (i = 0; i < numWorker; i++) {
struct nWorker *worker = (struct nWorker *)malloc(sizeof(struct nWorker));
if (worker == NULL) {
perror("malloc");
return -2;
}
// 置为0
memset(worker, 0, sizeof(struct nWorker));
// 员工可以访问线程池
worker->pool = pool;
worker->threadid = i;
worker->terminate = 0;
// 创建线程
int ret = pthread_create(&(worker->threadid), NULL, nThreadPoolCallback, worker);
// 创建失败返回非0
if (ret) {
perror("pthread_create");
free(worker);
worker = NULL;
return -3;
}
WORKER_INSERT(&worker, &(pool->workers));
}
// 成功
return 0;
}
销毁线程池
int nThreadPoolDestory(ThreadPool *pool, int nWorker) {
// 把每一个worker的terminate置为1即可
struct nWorker *worker = NULL;
for (worker = pool->workers; worker != NULL; worker = worker->next) {
worker->terminate = 1;
}
// 做一个广播,让处于等待状态的线程不再处于等待
// 因为线程处于等待状态的时候也使用了这把锁,所以为了防止后面线程等待再来抢这把锁而,这里需要占用锁
pthread_mutex_lock(&pool->mutex);
pthread_cond_broadcast(&pool->cond); // 唤醒所有
pthread_mutex_unlock(&pool->mutex);
pool->workers = NULL;
pool->tasks = NULL;
return 0;
}
向任务队列添加任务
int nThreadPoolPushTask(ThreadPool *pool, struct nTask *task) {
if (pool == NULL || task == NULL) {
return -1;
}
pthread_mutex_lock(&pool->mutex);
TASK_INSERT(&task, &(pool->tasks));
pthread_cond_signal(&pool->cond); // 唤醒一个线程
pthread_mutex_unlock(&pool->mutex);
}
线程回调函数
回调函数传入一个执行者,然后执行者从任务队列中取出一个任务,再执行. 拿营业厅来举例子,线程回调函数就像是员工在受理客户的请求,帮助客户完成业务这一过程.
// 线程回调函数
void *nThreadPoolCallback(void *arg) {
struct nWorker *worker = (struct nWorker*)arg;
while (1) {
pthread_mutex_lock(&worker->pool->mutex);
// 如果任务队列为空,线程处于等待状态
while (worker->pool->tasks == NULL) {
// 终止
if (worker->terminate) {
break;
}
printf("Thread is waiting\n");
pthread_cond_wait(&worker->pool->cond, &worker->pool->mutex);
}
pthread_mutex_unlock(&worker->pool->mutex);
// 再break掉一层
if (worker->terminate) {
// 先解锁,防止引发死锁
pthread_mutex_unlock(&worker->pool->mutex);
break;
}
// 如果任务队列不为空,就取出一个任务出来(首节点)
struct nTask *task = worker->pool->tasks;
if (task) {
TASK_REMOVE(&task, &(worker->pool->tasks));
}
if (task == NULL) {
continue;
}
// 开始处理这个任务
task->task_func(task);
}
if (worker != NULL) {
free(worker);
}
worker = NULL;
}
调用接口
#define THREADPOOL_INIT_COUNT 20
#define TASK_INIT_SIZE 50
// 客户要办理的业务
void task_entry(struct nTask *_task) {
struct nTask *task = _task;
int idx = *(int *)task->user_data;
printf("Execute Task: %d\n", idx);
free(task->user_data);
free(task);
}
int main() {
ThreadPool pool;
nThreadPoolCreate(&pool, THREADPOOL_INIT_COUNT);
int i = 0;
for (i = 0; i < TASK_INIT_SIZE; i++) {
struct nTask *task = (struct nTask *)malloc(sizeof(struct nTask));
// 一毫秒创建一个任务
usleep(1000);
if (task == NULL) {
perror("malloc");
exit(1);
}
memset(task, 0, sizeof(struct nTask));
task->task_func = task_entry;
task->user_data = malloc(sizeof(int));
*(int *)task->user_data = i;
nThreadPoolPushTask(&pool, task);
}
sleep(2);
// 释放线程池
nThreadPoolDestory(&pool, THREADPOOL_INIT_COUNT);
printf("\nnThreadPoolDestory\n");
}
调用结果
nThreadPoolCreate
Thread is waiting
Thread is waiting
Thread is waiting
Thread is waiting
Thread is waiting
Thread is waiting
Thread is waiting
Thread is waiting
Thread is waiting
Thread is waiting
Thread is waiting
Thread is waiting
Thread is waiting
Thread is waiting
Thread is waiting
Thread is waiting
Thread is waiting
Thread is waiting
Thread is waiting
Thread is waiting
Execute Task: 0
Thread is waiting
Execute Task: 1
Thread is waiting
Execute Task: 2
Thread is waiting
Execute Task: 3
Thread is waiting
Execute Task: 4
Thread is waiting
Execute Task: 5
Thread is waiting
Execute Task: 6
Thread is waiting
Execute Task: 7
Thread is waiting
Execute Task: 8
Thread is waiting
Execute Task: 9
Thread is waiting
Execute Task: 10
Thread is waiting
Execute Task: 11
Thread is waiting
Execute Task: 12
Thread is waiting
Execute Task: 13
Thread is waiting
Execute Task: 14
Thread is waiting
Execute Task: 15
Thread is waiting
Execute Task: 16
Thread is waiting
Execute Task: 17
Thread is waiting
Execute Task: 18
Thread is waiting
Execute Task: 19
Thread is waiting
Execute Task: 20
Thread is waiting
Execute Task: 21
Thread is waiting
Execute Task: 22
Thread is waiting
Execute Task: 23
Thread is waiting
Execute Task: 24
Thread is waiting
Execute Task: 25
Thread is waiting
Execute Task: 26
Thread is waiting
Execute Task: 27
Thread is waiting
Execute Task: 28
Thread is waiting
Execute Task: 29
Thread is waiting
Execute Task: 30
Thread is waiting
Execute Task: 31
Thread is waiting
Execute Task: 32
Thread is waiting
Execute Task: 33
Thread is waiting
Execute Task: 34
Thread is waiting
Execute Task: 35
Thread is waiting
Execute Task: 36
Thread is waiting
Execute Task: 37
Thread is waiting
Execute Task: 38
Thread is waiting
Execute Task: 39
Thread is waiting
Execute Task: 40
Thread is waiting
Execute Task: 41
Thread is waiting
Execute Task: 42
Thread is waiting
Execute Task: 43
Thread is waiting
Execute Task: 44
Thread is waiting
Execute Task: 45
Thread is waiting
Execute Task: 46
Thread is waiting
Execute Task: 47
Thread is waiting
Execute Task: 48
Thread is waiting
Execute Task: 49
Thread is waiting
nThreadPoolDestory