A thread pool implemented in modern C++.
代码已开源在 GitHub。
I. 引言
在现代软件开发中,多线程编程已经成为提高应用程序性能、实现并发任务处理的重要手段。然而,直接管理多个线程往往伴随着复杂性增加、资源浪费和同步问题。为了解决这些问题,线程池作为一种有效的线程管理机制应运而生。
线程池预先创建一组工作线程,将待执行任务提交到线程池,由池内线程负责任务的分配与执行,从而简化线程管理、减少系统频繁创建与销毁线程的开销、提高资源利用率。
当前现有的 C++ 线程池实现往往难以在简单易用性和文档的丰富性上取得平衡:
- 具有良好文档说明的 C++ 线程池项目常常规模庞大、依赖复杂,难以快速理解或上手使用;
- 实现简单的 C++ 项目往往缺乏足够的文档说明,开发者需要自行阅读源代码来理解其实现原理与使用方式。
此外对于像作者一样的以中文为母语的开发者(这一群体属实人数众多🥸)而言,我们常常感到缺乏足够的拥有中文文档的项目可供研究(甚至很多项目即使是由中国开发者开发的,它们的文档仍然是英文🥲),往往需要被迫使用英语等非母语语言进行文档的阅读,不能达到最佳效率。
针对上述问题,作者希望能够实现一个简单易用并且具有丰富中/英文文档说明的 C++ 线程池库,以期能够助力于开发者社区的学习、研究与使用。
代码已开源在 GitHub。
线程池的优势
资源管理与性能优化
- 避免频繁创建与销毁线程:线程池预先创建并维护一定数量的工作线程,避免了频繁创建和销毁线程带来的系统开销,特别是在处理大量短生命周期任务时,效果尤为显著。
- 均衡负载与缓存局部性:线程池可以根据任务负载动态调整线程工作状态,避免过度竞争和闲置。同时,线程在执行任务过程中可以充分利用CPU缓存,提高执行效率。
- 控制并发级别:通过限制线程池大小和任务队列容量,可以有效控制系统的并发级别,防止因过度并发导致的资源争抢和性能下降。
简化编程模型
- 统一任务提交接口:线程池提供统一的接口供开发者提交任务,无需关心线程创建、同步等底层细节,降低了多线程编程的复杂度。
- 异常处理与任务取消:线程池通常支持异常处理机制和任务取消功能,使得在出现异常情况或需求变更时,可以更方便地管理和调整任务执行。
- 但本项目目前还未针对这一方面进行特别的实现🥲。
II. 设计概览
库架构
线程池采用了模块化设计,主要由以下几个核心组件构成:
-
thread_pool类:作为用户直接交互的接口,负责任务调度、线程管理等核心功能。用户通过创建thread_pool实例提交任务,控制线程池状态,并获取线程池相关信息。点此查看
thread_pool类的 API 参考文档。 -
thread_pool::worker_thread类:作为线程池内部的工作单元,每个thread_pool::worker_thread对象代表一个独立的工作线程,负责从任务队列中取出任务并执行。点此查看
thread_pool::worker_thread类的 API 参考文档。 -
辅助工具:包括同步原语(如互斥锁、条件变量、信号量等)以及状态管理机制,它们为线程池和工作线程之间的通信、任务同步、状态变更等操作提供了必要的支撑。
各组件间的关系如下:
thread_pool类维护一个工作线程列表std::list<worker_thread>,并通过同步原语控制任务队列的访问与状态变更。thread_pool::worker_thread类通过与thread_pool对象的交互,获取待执行任务、更新自身状态,并响应来自thread_pool的指令(如暂停、恢复、终止等)。- 辅助工具贯穿于整个库的设计与实现中,确保并发环境下的数据一致性与操作安全性。
关键类与接口
thread_pool 类
功能定位:thread_pool 类是用户与线程池库交互的主要入口,封装了线程池创建、任务提交、状态控制、属性调整与信息获取等核心功能。
主要API:
-
构造函数:接受初始工作线程数和最大任务队列容量作为参数,创建并初始化线程池。
-
任务提交:提供模板方法
submit,接受可调用对象(如函数、lambda 表达式)及参数,将其封装为任务提交至任务队列,并返回一个std::future对象,用于获取任务执行结果。 -
线程池控制:
pause:暂停线程池,阻止新任务的执行并暂停当前运行的任务。resume:恢复线程池的运行,继续处理队列中的任务。shutdown:设置线程池为等待任务完成状态,等待所有已提交任务执行完毕后终止线程池。shutdown_now:立即终止线程池,丢弃任务队列中的未处理任务。
-
属性调整与信息获取:
add_thread:动态向线程池添加工作线程。remove_thread:从线程池中移除指定数量的工作线程。set_max_task_count:限制任务队列中允许的最大任务数量。get_thread_count:查询当前线程池中活动的工作线程数。get_task_count:查询等待执行的任务数量。
thread_pool::worker_thread 类(内部实现)
功能定位:thread_pool::worker_thread 类是线程池内部的工作单元,负责从任务队列中取出任务并执行。其状态机模型、任务循环逻辑以及响应线程池指令的方式直接影响线程池的性能与稳定性。
内部状态与行为:
- 状态机:
thread_pool::worker_thread对象具有多种状态(如运行、暂停、等待任务、将终止、已终止等),通过状态机模型管理状态变迁。 - 任务循环:每个工作线程在一个无限循环中,根据自身状态执行相应操作,如从任务队列取任务、执行任务、响应线程池指令(如暂停、恢复、终止)等。
- 响应指令:通过监听线程池状态变更信号和条件变量,
thread_pool::worker_thread能够及时响应来自thread_pool的指令,调整自身状态并执行相应操作。
III. 实现细节剖析
线程池核心机制
任务队列
数据结构选择:任务队列采用 std::queue 实现,它是一种先进先出(FIFO)的数据结构,符合线程池任务调度的基本原则——按提交顺序依次执行。
同步机制:
-
互斥锁:使用
std::shared_mutex保护任务队列的访问,确保在多线程环境下对任务队列的操作是线程安全的。读取任务队列状态(如查询任务数量)时使用std::shared_lock,仅需读取权限;向队列添加或移除任务时使用std::unique_lock,需要独占访问权限。 -
条件变量:使用
std::condition_variable_any协调线程间的同步。当任务队列为空时,等待任务的线程会被阻塞,直到有新任务入队或线程池状态发生改变时,通过条件变量唤醒等待的线程。
工作线程管理
创建与销毁:在 thread_pool 构造函数中创建指定数量的工作线程,并将它们加入工作线程列表。线程池析构时,通过调用thread_pool::worker_thread的terminate方法终止所有工作线程,并等待其退出。
状态切换:
-
线程状态:使用
std::atomic<>存储线程状态,确保在多线程环境下状态变更的原子性。std::atomic<>是 C++ 标准库中的原子变量类型,可以保证对该类型的对象的读/写等操作是原子的。
-
同步原语:
- 互斥锁:保护线程状态的访问,确保状态变更操作的原子性。
- 条件变量:用于工作线程等待任务、响应线程池指令等场景,实现线程间的同步。
- 信号量:在
thread_pool::worker_thread类中使用std::binary_semaphore实现线程的暂停与恢复。

本文介绍了一个用现代C++实现的线程池库,该库已开源在GitHub。阐述了线程池在资源管理、性能优化和简化编程模型方面的优势,详细剖析了库架构、关键类与接口、实现细节,还介绍了库特性与优化、示例代码、性能测试,并对未来功能扩展提出展望。
最低0.47元/天 解锁文章
861






