文章目录
一、线程池的原理和实现
1. 线程池是什么?
一种维持管理固定数量线程的的池式结构。
(其他池式结构:数据库连接池、内存池、对象池, 共同点:复用资源)
线程池预先创建一定数量的线程,当有任务提交时,线程池会从空闲线程中选取一个来执行该任务;若没有空闲线程,任务会被放入任务队列等待;当线程完成任务后,不会被销毁,而是继续等待下一个任务,这样可以避免频繁创建和销毁线程带来的开销。
线程池的作用 (为什么需要线程池)
某类任务特别耗时,严重影响该线程处理其他任务,因此想到把这类任务放在其他线程异步执行。
线程池的作用:
1. 复用线程资源,减少线程创建和销毁的开销
2. 可异步执行耗时任务,性能优化,不过度占用核心线程
3. 并发执行核心业务,充分利用多核资源,减少了多任务的执行时间
线程池的构成(生产者-任务队列-消费者线程)
线程池属于一种生产消费模型,线程池运行环境构成:
1. 生产者线程:发布任务
2. 任务队列:存储任务,调度线程池
3. 消费者线程:取出任务,执行任务

2. 为什么维持管理固定数量的线程池?
维持固定数量的线程池作用
1.由于系统资源的限制,随着线程数量继续增加,不再带来性能的提升反而带来负担。
2.避免线程频繁的创建和销毁,消耗资源。
线程池中线程数量如何确定(经验公式:CPU核数)
依据 “ 充分利用系统资源 ”
CPU密集型 : CPU核心数
IO密集型(网络IO,磁盘IO):2 * CPU核心数
=> (线程等待时间 + cpu运算时间)* cpu 核心数 / cpu运算时间
3*. 线程池怎么解决问题的? (拓展)
-
初始化阶段
配置参数:开发者需要根据系统的资源状况、任务特性等因素,为线程池配置关键参数。比如最大线程数,它限定了线程池同时能运行的线程上限,避免系统因创建过多线程而耗尽资源;最小线程数规定了线程池初始创建并保持的线程数量;任务队列大小则决定了在所有线程都忙碌时,可暂存任务的数量。创建线程:依据配置好的最小线程数,线程池开始预先创建相应数量的线程。这些线程在创建后处于就绪状态,随时等待执行任务,这一操作减少了后续任务到来时的线程创建时间,提升了系统响应速度。
-
任务提交阶段
接收任务:当有新任务提交到线程池时,线程池会对任务进行接收和管理。任务可以是各种类型的计算任务、I/O 操作等。分配任务:线程池会检查当前是否有空闲线程。若存在空闲线程,就会立即将任务分配给其中一个空闲线程执行;若所有线程都在忙碌,任务会被放入任务队列中等待。
-
任务执行阶段
线程执行任务:获得任务的线程开始执行具体的任务逻辑。在执行过程中,线程会利用系统资源完成任务,由于线程是预先创建好的,避免了频繁创建和销毁线程带来的开销。异常处理:若线程在执行任务时抛出异常,线程池会捕获该异常并进行相应处理。通常会将异常信息记录下来,同时将该线程标记为空闲状态,以便后续继续执行其他任务,保证系统的稳定性。
-
任务队列管理阶段
任务排队:当任务队列中有任务等待,且线程池中有空闲线程时,线程池会从任务队列中取出一个任务分配给空闲线程执行,确保任务按照一定的顺序得到处理。队列满处理:若任务队列已满,且所有线程都在忙碌,此时新提交的任务可能会根据线程池的配置策略进行处理,比如拒绝执行任务并抛出异常,或者等待队列中有任务完成后再加入队列。
-
线程管理阶段
线程复用:线程完成任务后,不会被销毁,而是返回到线程池的空闲线程列表中,等待下一个任务的分配。这样可以实现线程的复用,减少系统资源的消耗。线程调整:线程池会根据系统的负载情况和任务的执行情况,动态调整线程的数量。例如,当任务量突然增加,且任务队列中的任务数量达到一定阈值时,线程池可能会创建新的线程来处理任务;当任务量减少,空闲线程过多时,线程池可能会销毁一些多余的线程,以节省系统资源。
-
关闭阶段
停止接收新任务:当系统不再需要线程池处理任务时,会通知线程池停止接收新的任务。
完成剩余任务:线程池会继续执行任务队列中剩余的任务,直到所有任务都执行完毕。
销毁线程:在所有任务执行完成后,线程池会销毁所有线程,释放系统资源。
4. 手撕线程池
* 封装原则 (隐藏实现细节,暴露使用接口)
- 隐藏实现细节,暴露使用接口, 客户知道的越少越好。
用户是否需要知道线程池结构
用户是否需要知道队列
用户是否需要知道任务以何种形式执行
用户是否需要知道任务结构
用户是否需要知道线程管理的细节
- 编码对称接口设计
资源创建时,回滚式编程
流程处理时,防御式编程
1. 数据结构设计
-
任务结构
typedef struct task_s { void *next; //指针指向下一个任务 //任务的处理函数指针,当任务被线程执行时,会调用这个函数 handler_pt func; void *arg; //函数参数(上下文) } task_t; -
阻塞队列 (任务队列)
typedef struct task_queue_s { void *head; void **tail; int block;//阻塞/非阻塞 // 加锁实现对临界资源访问的保护 spinlock_t lock;//自旋锁 pthread_mutex_t mutex;//互斥锁 pthread_cond_t cond;//信号量 } task_queue_t; ============================================== + block: 表示队列是否阻塞。为 1 时,队列处于阻塞状态,等待任务到来;为 0 时,不阻塞,即用于线程退出时唤醒所有等待线程。 + spinlock_t lock: 自旋锁,用于保护队列的链表操作,保证对head和tail的并发访问安全。 + pthread_mutex_t mutex; 与 pthread_cond_t cond: 互斥锁与条件变量用于实现等待和唤醒机制。当队列中没有任务时,工作线程会进入等待状态;有新任务时,通过条件变量唤醒等待线程。 -
线程池的结构
struct thrdpool_s { task_queue_t *task_queue; //任务队列 atomic_int quit; //原子变量 int thrd_count; //线程数量 pthread_t *threads; //线程数组 }; ============================================== + atomic_int quit: 线程池是否退出的标志,通过原子操作保证在多线程环境下的安全性。当设置为 1 时,表示线程池正在退出或已经退出。 + thrd_count: 表示线程池中成功创建并运行的线程数量。 + pthread_t *threads:用于存储线程 ID 的数组,用于后续 join 等操作。
2. 接口设计

1. thrdpool_create(int thrd_count)
创建并初始化一个线程池对象。
它接受一个参数 thrd_count,表示线程池中要创建的线程数量。
内部会创建一个任务队列,用于存储待执行的任务,并初始化线程池中的线程。
线程池的线程会等待并执行队列中的任务。
// 创建并初始化一个线程池对象
thrdpool_t *thrdpool_create(int thrd_count) {
thrdpool_t *pool;
pool = (thrdpool_t*)malloc(sizeof(*pool)); // 分配线程池结构体
if (pool) {
task_queue_t *queue = __taskqueue_create(); // 创建任务队列
if (queue) {
pool->task_queue = queue;
atomic_init(&pool->quit, 0); // 初始化退出标志
if (__threads_create(pool, thrd_count) == 0) // 创建线程
return pool;
__taskqueue_destroy(queue); // 任务队列创建失败时销毁
}
free(pool); // 线程池创建失败时释放内存
}
return NULL; // 返回NULL表示创建失败
}
========================================================
2.thrdpool_post(thrdpool_t *pool, handler_pt func, void *arg)
向线程池提交一个任务,任务由指定的处理函数 func 和任务参数 arg 组成。
该函数会将任务添加到线程池的任务队列中。
// 向线程池提交任务,将任务添加到任务队列中
int thrdpool_post(thrdpool_t *pool, handler_pt func, void *arg) {
if (atomic_load(&pool->quit) == 1) // 如果线程池已关闭,则不再接受新任务
return -1;
task_t *task = (task_t*) malloc(sizeof(task_t)); // 为任务分配内存
if (!task) return -1; // 内存分配失败
task->func = func; // 设置任务的处理函数
task->arg = arg; // 设置任务的参数
__add_task(pool->task_queue, task); // 将任务添加到队列中
return 0; // 成功返回0
}
========================================================
3.thrdpool_terminate(thrdpool_t *pool)
终止线程池,停止所有线程的执行.它会设置 quit 标志为 1,并且使任务队列变为非阻塞状态,从而通知所有工作线程退出。
// 终止线程池,停止所有线程的执行
void thrdpool_terminate(thrdpool_t *pool) {
atomic_store(&pool->quit, 1); // 设置退出标志,通知线程池关闭
__nonblock(pool->task_queue); // 设置任务队列为非阻塞模式,允许线程退出
}
============================================

:线程池&spm=1001.2101.3001.5002&articleId=149772601&d=1&t=3&u=d6e7e12f536a4951bc6105ae55fba1c7)
3784

被折叠的 条评论
为什么被折叠?



