1. 线程池概述
线程池是一种预先创建多个线程,并将其统一管理的机制。在高并发环境下,通过复用线程来执行任务,线程池能够有效降低线程创建和销毁的频率,减少系统开销,同时提高系统的响应速度和吞吐量。此外,线程池还能对并发数进行限制,避免因线程数过多导致的资源竞争问题。
2. 代码结构概览
在所提供的代码中,主要包含两个结构体:
- Task结构体:封装了任务函数指针及其参数,定义了线程池中每个任务的基本信息。
- threadPool结构体:用于管理线程池的各项数据,包括最小和最大线程数、当前活跃线程数、忙碌线程数、任务队列(及其索引)、以及各种同步机制(互斥锁与条件变量)。
通过这种设计,可以较好地管理任务队列与线程状态,实现线程池的动态调节。
3. 线程池初始化与线程管理
3.1 初始化过程
在create_threadPool
函数中:
- 为线程池、任务池和工作线程数组动态分配内存。
- 初始化互斥锁和条件变量,保证后续多线程操作的同步安全。
- 创建管理线程(manager)和初始工作线程(worker)。管理线程负责监控任务量和线程状态,而工作线程则负责执行任务。
这种设计确保线程池在启动时就具备基本的任务处理能力,同时预留了后续扩展的可能。
3.2 管理线程(Manager Thread)
管理线程周期性(代码中每隔3秒)检查任务队列与工作线程的状态:
- 当任务数量大于现有线程数且未达到最大线程限制时,管理线程会增加新的工作线程(每次增加的数量由
reamke_count
决定)。 - 当系统中空闲线程过多(即忙碌线程数量较低)且活跃线程数大于最小值时,会通过设置销毁线程数标识(
exitnum
)并发出信号,通知部分空闲线程退出。
这种动态扩缩容策略使线程池能够根据实际负载自动调节线程数量,既保证了高峰期的并发处理能力,又避免了资源浪费。
3.3 工作线程(Worker Thread)
工作线程在一个无限循环中执行以下操作:
- 先获取互斥锁并检查任务队列是否为空或线程池是否处于关闭状态。
- 如果任务队列为空,则通过条件变量
isEmpty
等待任务的到来;同时,在等待期间会检测是否存在多余线程需要销毁。 - 当有任务可执行时,从任务队列中取出任务,并在执行前更新忙碌线程计数。
- 执行任务函数,并在任务完成后释放任务参数内存,再更新忙碌线程计数。
这种设计既保证了任务的及时调度,又确保了线程退出时能正确更新线程池状态,从而实现了线程池的高效协作。
3.4 任务添加与同步控制
任务的添加通过add_task
函数完成,该函数的主要流程包括:
- 获取线程池锁,判断任务队列是否已满。如果满,则通过
isFULL
条件变量等待。 - 将任务插入任务数组,并更新任务尾索引和任务计数。
- 发出条件变量
isEmpty
信号,唤醒等待任务的工作线程。
这种机制利用了生产者—消费者模型,确保任务生产与消费之间的协调,从而避免竞争和死锁问题。
4. 线程池销毁机制
线程池的销毁由threadPool_destroy
函数实现。主要步骤如下:
- 将线程池状态标记为关闭(
is_shutted
置1),通知所有线程即将退出。 - 通过
pthread_join
等待管理线程结束,并发信号唤醒所有等待的工作线程,促使它们检查关闭标志后退出。 - 最后依次释放任务数组、工作线程数组以及销毁所有同步机制,确保资源得到充分清理。
这一机制保证了线程池在退出前能安全地终止所有线程,避免内存泄漏和悬挂线程问题。
5. 同步机制与线程安全
代码中使用了多种同步手段:
- 互斥锁(Mutex):用于保护线程池中共享数据的读写,防止数据竞争。
- 条件变量(Condition Variable):
isEmpty
用于控制任务等待,isFULL
用于控制任务生产。条件变量使线程能够在满足特定条件时被唤醒,提高了资源利用率。 - 忙碌计数与退出标识:通过
busynum
和exitnum
记录线程工作状态和销毁需求,实现了对线程数量的精细调控。
这些同步机制确保了多线程环境下的数据一致性和系统的稳定性。
6.头文件
#ifndef __threadpool_H
#define __threadpool_H
#include <pthread.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
// 任务结构体:封装了任务函数及其参数
typedef struct Task {
void (*func)(void* arg);
void* arg;
} Task;
// 线程池结构体:保存线程池状态、任务队列、线程ID数组及同步机制
typedef struct threadPool {
int min; // 最小线程数
int max; // 最大线程数
int livenum; // 当前存活线程数
int busynum; // 正在执行任务的线程数
int exitnum; // 需要销毁的线程数
Task* tasks; // 任务队列(数组形式存储任务)
int task_total; // 任务队列的总容量
int task_nums; // 当前任务数量
int task_head; // 队首索引(取任务的位置)
int task_end; // 队尾索引(添加任务的位置)
pthread_t manger_Id; // 管理线程的ID
pthread_t *worker_Ids; // 工作线程的ID数组
// 互斥锁和条件变量,用于线程间同步和保护共享数据
pthread_mutex_t threadPool_mutex;
pthread_mutex_t busy_mutex;
pthread_cond_t isFULL; // 条件变量:任务队列是否已满
pthread_cond_t isEmpty; // 条件变量:任务队列是否为空
int is_shutted; // 标记线程池是否关闭
} threadPool;
// 函数声明
threadPool* create_threadPool(int min, int max, int task_total);
void* manager(void* arg);
void* worker(void* arg);
void thread_exit(threadPool* pool);
int check_busynums(threadPool* pool);
int check_livenums(threadPool* pool);
void add_task(threadPool* pool, void (*func)(void* arg), void* arg);
void threadPool_destroy(threadPool* pool);
#endif
7.线程池实现
// ------------------- 线程池实现 -------------------
#include "threadpool.h"
#define reamke_count 3 // 每次增减的工作线程数量
// 创建线程池,初始化所有数据并启动管理线程和工作线程
threadPool* create_threadPool(int min, int max, int task_total) {
threadPool* pool = (threadPool*)malloc(sizeof(threadPool));
if (pool == NULL) {
printf("线程池初始化失败,请检查问题\n");
return NULL;
}
pool->tasks = (Task*)malloc(sizeof(Task) * task_total);
if (pool->tasks == NULL) {
printf("任务池初始化失败,请检查问题\n");
free(pool);
return NULL;
}
pool->worker_Ids = (pthread_t*)calloc(max, sizeof(pthread_t));
pool->manger_Id = 0;
if (pool->worker_Ids == NULL) {
printf("工人线程初始化失败\n");
free(pool->tasks);
free(pool);
return NULL;
}
// 初始化互斥锁和条件变量
if (pthread_mutex_init(&pool->threadPool_mutex, NULL) != 0 ||
pthread_mutex_init(&pool->busy_mutex, NULL) != 0 ||
pthread_cond_init(&pool->isEmpty, NULL) != 0 ||
pthread_cond_init(&pool->isFULL, NULL) != 0) {
printf("锁初始化失败\n");
free(pool->tasks);
free(pool->worker_Ids);
free(pool);
return NULL;
}
// 初始化线程池状态
pool->busynum = 0;
pool->exitnum = 0;
pool->livenum = min;
pool->max = max;
pool->min = min;
pool->task_total = task_total;
pool->task_nums = 0;
pool->task_head = 0;
pool->task_end = 0;
pool->is_shutted = 0;
// 创建管理线程,负责动态调整工作线程数
int ret = pthread_create(&pool->manger_Id, NULL, manager, pool);
if (ret != 0) {
printf("管理者线程创建失败,错误码: %d\n", ret);
free(pool->tasks);
free(pool->worker_Ids);
free(pool);
return NULL;
}
// 根据最小线程数创建工作线程
for (int i = 0; i < pool->min; ++i) {
ret = pthread_create(&pool->worker_Ids[i], NULL, worker, pool);
if (ret != 0) {
printf("工作线程创建失败,错误码: %d\n", ret);
free(pool->tasks);
free(pool->worker_Ids);
free(pool);
return NULL;
}
}
return pool;
}
// 工作线程函数:不断等待并执行任务
void* worker(void* arg) {
threadPool* pool = (threadPool*)arg;
while (1) {
pthread_mutex_lock(&pool->threadPool_mutex);
// 如果任务队列为空且线程池未关闭,则等待任务
if (pool->task_nums == 0 && !pool->is_shutted) {
pthread_cond_wait(&pool->isEmpty, &pool->threadPool_mutex);
// 判断是否有多余线程需要退出
if (pool->exitnum > 0) {
pool->exitnum--;
if (pool->livenum > pool->min) {
pool->livenum--;
thread_exit(pool);
}
}
}
// 如果线程池处于关闭状态,则退出
if (pool->is_shutted) {
thread_exit(pool);
}
// 从任务队列中取出一个任务
Task task = pool->tasks[pool->task_head];
pool->task_head = (pool->task_head + 1) % pool->task_total;
pool->task_nums--;
// 通知添加任务的线程:队列中有空位了
pthread_cond_signal(&pool->isFULL);
pthread_mutex_unlock(&pool->threadPool_mutex);
printf("工人:%ld 正在工作\n", pthread_self());
// 更新忙碌线程计数
pthread_mutex_lock(&pool->busy_mutex);
pool->busynum++;
pthread_mutex_unlock(&pool->busy_mutex);
// 执行任务
task.func(task.arg);
// 任务完成后释放任务参数内存
free(task.arg);
printf("工人:%ld 已经完成工作\n", pthread_self());
// 更新忙碌线程计数
pthread_mutex_lock(&pool->busy_mutex);
pool->busynum--;
pthread_mutex_unlock(&pool->busy_mutex);
}
return NULL;
}
// 管理线程函数:定期检查任务队列和线程状态,动态增减工作线程
void* manager(void* arg) {
threadPool* pool = (threadPool*)arg;
while (1) {
sleep(3);
if (!pool->is_shutted) {
pthread_mutex_lock(&pool->threadPool_mutex);
int livenum = pool->livenum;
int tasknums = pool->task_nums;
pthread_mutex_unlock(&pool->threadPool_mutex);
// 若任务数大于当前线程数且未达到最大线程数,则添加新的工作线程
if (tasknums > livenum && livenum < pool->max) {
pthread_mutex_lock(&pool->threadPool_mutex);
int count = 0;
for (int i = 0; i < pool->max && livenum < pool->max && count < reamke_count; ++i) {
if (pool->worker_Ids[i] == 0) {
pthread_create(&pool->worker_Ids[i], NULL, worker, pool);
pool->livenum++;
count++;
}
}
pthread_mutex_unlock(&pool->threadPool_mutex);
}
// 若空闲线程较多,则减少部分工作线程
pthread_mutex_lock(&pool->busy_mutex);
int busynums = pool->busynum;
pthread_mutex_unlock(&pool->busy_mutex);
if (busynums * 2 < pool->livenum && livenum > pool->min) {
pthread_mutex_lock(&pool->threadPool_mutex);
pool->exitnum = reamke_count;
pthread_mutex_unlock(&pool->threadPool_mutex);
for (int i = 0; i < reamke_count; ++i) {
pthread_cond_signal(&pool->isEmpty);
}
}
}
}
return NULL;
}
// 添加任务到任务队列,使用条件变量保证队列不溢出
void add_task(threadPool* pool, void (*func)(void* arg), void* arg) {
pthread_mutex_lock(&pool->threadPool_mutex);
// 如果任务队列已满,则等待
while (!pool->is_shutted && pool->task_nums == pool->task_total) {
pthread_cond_wait(&pool->isFULL, &pool->threadPool_mutex);
}
if (pool->is_shutted) {
pthread_mutex_unlock(&pool->threadPool_mutex);
return;
}
// 将任务添加到队尾
pool->tasks[pool->task_end].func = func;
pool->tasks[pool->task_end].arg = arg;
pool->task_end = (pool->task_end + 1) % pool->task_total;
pool->task_nums++;
// 通知等待任务的工作线程
pthread_cond_signal(&pool->isEmpty);
pthread_mutex_unlock(&pool->threadPool_mutex);
}
// 检查当前忙碌的线程数
int check_busynums(threadPool* pool) {
int val = 0;
pthread_mutex_lock(&pool->busy_mutex);
val = pool->busynum;
pthread_mutex_unlock(&pool->busy_mutex);
return val;
}
// 检查当前存活的线程数
int check_livenums(threadPool* pool) {
int val = 0;
pthread_mutex_lock(&pool->threadPool_mutex);
val = pool->livenum;
pthread_mutex_unlock(&pool->threadPool_mutex);
return val;
}
// 工作线程退出函数:将自己的线程ID置0,并调用pthread_exit退出
void thread_exit(threadPool* pool) {
int id = pthread_self();
for (int i = 0; i < pool->max; ++i) {
if (id == pool->worker_Ids[i]) {
pool->worker_Ids[i] = 0;
printf("我被迫辞职不干 我是工人:%ld\n", pthread_self());
break;
}
}
pthread_exit(NULL);
}
// 销毁线程池:关闭所有线程、释放所有资源
void threadPool_destroy(threadPool* pool) {
if (pool == NULL) {
return;
}
pool->is_shutted = 1;
pthread_join(pool->manger_Id, NULL);
// 唤醒所有等待中的工作线程,使其检查关闭标志后退出
for (int i = 0; i < pool->livenum; ++i) {
pthread_cond_signal(&pool->isEmpty);
}
// 释放任务队列和工作线程数组
if (pool->tasks) free(pool->tasks);
if (pool->worker_Ids) free(pool->worker_Ids);
// 销毁互斥锁和条件变量
pthread_mutex_destroy(&pool->threadPool_mutex);
pthread_mutex_destroy(&pool->busy_mutex);
pthread_cond_destroy(&pool->isEmpty);
pthread_cond_destroy(&pool->isFULL);
// 释放线程池结构体
free(pool);
pool = NULL;
return;
}
8.主函数
#include "threadpool.h"
// 示例任务函数:输出线程ID和传入的数字,模拟工作1秒
void taskFunc(void* arg) {
int num = *(int*)arg;
printf("thread %ld is working, number = %d\n", pthread_self(), num);
sleep(1);
}
int main() {
// 注意:main函数中使用的接口名称与上面定义的名称略有不一致,
// 实际使用时应保持统一,这里假设你在调用时做了相应的适配。
threadPool* pool = create_threadPool(5, 50, 100);
for (int i = 0; i < 100; ++i) {
int* num = (int*)malloc(sizeof(int));
*num = i + 100;
add_task(pool, taskFunc, num);
}
// 让线程池运行一段时间,等待所有任务完成
sleep(30);
threadPool_destroy(pool);
return 0;
}
9、总结
本文实现了一个基于C语言的简易线程池,涵盖了任务队列、工作线程管理、任务添加、线程池销毁等核心模块。通过对比常见的线程池设计,读者可以更深入理解线程池的底层机制。实际开发中,可以根据具体需求对线程池进行优化和扩展,如调整任务队列的大小、动态调整线程数量等。