C++17线程池神器:BS::thread_pool 极速多核并行

内容分享1周前发布
7 2 0

介绍

BS::thread_pool 是一个高效、轻量级的现代 C++ 线程池库,由 Barak Shoshany 开发,专为高性能计算设计。该库于 2024 年 12 月 19 日发布 v5.0.0 版本,支持 C++17、C++20 和 C++23 标准。它提供了一个简单的线程池实现,用于管理固定数量的线程,这些线程从任务队列中持续获取并执行任务,避免了频繁创建和销毁线程的开销。

这个库的诞生源于作者在科学计算项目中的需求,例如处理高性能计算节点上的多核任务。它强调四个核心原则:紧凑性(单头文件)、可移植性(仅依赖标准库)、易用性(详细文档和简单 API)和性能(针对多核优化)。与其他线程池库相比,BS::thread_pool 处于中间位置:不像小型库那样功能简陋,也不像大型库那样依赖复杂组件。它已被数千名 C++ 开发者用于科学计算、游戏开发等领域。

库的核心类是 BS::thread_pool,支持提交任务、并行化循环、暂停/恢复线程等功能。通过 std::future,用户可以轻松等待任务完成并获取返回值。库的总代码量仅 487 行(不含注释),但功能全面,支持包管理器安装(如 vcpkg、Conan)。如果您正在开发需要多线程的应用程序,这个库能显著提升效率。

C++17线程池神器:BS::thread_pool 极速多核并行

C++17线程池神器:BS::thread_pool 极速多核并行

特性

BS::thread_pool 的特性设计注重性能和易用性,以下是其主要亮点:

  • 高性能:从零构建,针对最大性能优化。线程复用避免创建/销毁开销,任务队列确保不超过硬件并发度。支持 C++20/23 新特性,进一步提升效率。在高性能计算节点上表现优秀,经多平台基准测试验证。
  • 轻量级:单头文件(BS_thread_pool.hpp),无外部依赖。头文件仅模式,无需构建或安装。只需 #include 或 import BS.thread_pool(C++20 模块)。代码紧凑,总行数少,便于集成。
  • 现代与可移植:兼容 C++17+ 标准库,无编译器扩展或第三方库。支持 Windows、Linux、macOS,以及 Clang、GCC、MSVC。C++20 支持模块导入,C++23 支持 import std 加速编译。
  • 易用性:API 简单,仅几个成员函数即可基本使用。详细文档(README.md 超过 3000 行),包括示例和 Doxygen 注释。支持 lambda 表达式提交任务,自动处理异常。
  • 任务管理:提交任务返回 std::future,支持无返回值的 detach_task。循环并行化(submit_loop/detach_loop)自动分割块,优化负载均衡。
  • 可选特性:通过模板参数启用优先级(-128 到 +127)、暂停/恢复、死锁检查。优先级确保高优先任务先执行,暂停允许动态控制线程。
  • 附加工具:BS::multi_future 处理多个 future;BS::synced_stream 同步多线程输出;BS::this_thread 获取线程索引/池指针。
  • 测试与基准:附带测试程序 BS_thread_pool_test.cpp,进行数百项自动化测试和 Mandelbrot 分形基准。支持多编译器测试脚本。
  • 原生扩展:可选启用(宏定义),提供 OS 级亲和性、优先级设置(进程/线程),支持 Windows/Linux/macOS。

这些特性使库适用于从简单脚本到复杂科学模拟的场景,性能不输大型库,却更易上手。

C++17线程池神器:BS::thread_pool 极速多核并行

C++17线程池神器:BS::thread_pool 极速多核并行

架构

BS::thread_pool 的架构简洁高效,由核心类和辅助组件组成。核心是 BS::thread_pool 类模板,管理线程池和任务队列。以下按模块分类详细说明:

1. 核心线程池类(BS::thread_pool)

  • 构造函数:默认使用硬件并发度(如 std::thread::hardware_concurrency())创建线程。支持指定线程数和初始化函数(每个线程运行一次)。
  • 重置与获取:reset() 动态调整线程数;get_thread_count() 获取线程数;get_thread_ids() 获取线程 ID 向量。
  • 任务提交模块
    • submit_task(F&& task):提交无参数任务,返回 std::future<R>(R 为返回值类型)。
    • detach_task(F&& task):提交任务但不返回 future,节省开销。
    • 支持 lambda 捕获参数。
  • 循环并行化模块
    • submit_loop/detach_loop:自动分割循环为块,每个块作为任务。函数接受索引 i。
    • submit_blocks/detach_blocks:用户手动处理块范围 [start, end),更高效。
    • submit_sequence/detach_sequence:每个索引独立任务,无块分割。
    • 返回 BS::multi_future 处理多个 future。
  • 等待模块
    • wait():等待所有任务完成。
    • wait_for/wait_until:带超时等待,返回 bool 表明是否全部完成。
  • 可选模块(模板参数 BS::tp::xxx):
    • 优先级:提交时指定优先级(BS::priority_t),高优先先执行。
    • 暂停:pause()/unpause()/is_paused() 控制线程是否取任务。
    • 死锁检查:wait() 等抛 BS::wait_deadlock 异常避免死锁。
  • 析构:等待任务完成,销毁线程。

2. 辅助类

  • BS::multi_future<T>:std::vectorstd::future<T> 的特化。支持 get() 获取所有值、wait() 等待所有、ready_count() 检查就绪数。
  • BS::synced_stream:同步输出流。print()/println() 确保多线程安全打印,支持多个 std::ostream。
  • BS::this_thread:静态函数 get_index() 获取线程索引、get_pool() 获取池指针。原生扩展添加 OS 线程亲和性/名称/优先级设置。
  • BS::version:版本号类,支持比较和字符串转换。库版本 BS::thread_pool_version。

3. 全局函数与枚举

  • 原生扩展函数:set_os_process_affinity() 等设置进程/线程亲和性和优先级。
  • 枚举:BS::tp(模板标志)、BS::pr(预定义优先级)、BS::os_process_priority/BS::os_thread_priority(OS 优先级)。

架构使用条件变量、互斥锁和队列实现任务同步。线程在后台循环取任务,优先级用优先队列实现。模块化设计允许用户启用/禁用特性,保持轻量。

快速上手

安装

  • 手动下载:从 GitHub 下载 BS_thread_pool.hpp,置于项目文件夹。#include “BS_thread_pool.hpp”。
  • C++20 模块:使用 BS.thread_pool.cppm,import BS.thread_pool;。需预编译模块。
  • 包管理器
    • vcpkg: vcpkg install bshoshany-thread-pool
    • Conan: 添加 [requires] bshoshany-thread-pool/5.0.0
    • Meson: meson wrap install bshoshany-thread-pool
    • CMake/CPM: CPMAddPackage(“gh:bshoshany/thread-pool@5.0.0”)

编译

使用 C++17+ 标准。示例命令(GCC):

 g++ your_program.cpp -std=c++17 -O3 -pthread -o your_program

C++20 模块需额外旗帜,如 Clang: -fmodules。

基本示例

创建池并提交任务:

 #include "BS_thread_pool.hpp"
 #include <future>
 #include <iostream>
 
 int main() {
     BS::thread_pool pool;  // 默认硬件并发线程
     auto future = pool.submit_task([] { return 42; });
     std::cout << future.get() << std::endl;  // 输出 42
     return 0;
 }

detach 任务:

 #include "BS_thread_pool.hpp"
 #include <chrono>
 #include <iostream>
 #include <thread>
 
 int main() {
     BS::thread_pool pool;
     int result = 0;
     pool.detach_task([&result] {
         std::this_thread::sleep_for(std::chrono::milliseconds(100));
         result = 42;
     });
     pool.wait();  // 等待所有任务
     std::cout << result << std::endl;  // 输出 42
     return 0;
 }

启用优先级(模板参数):

 #include "BS_thread_pool.hpp"
 #include <iostream>
 
 int main() {
     BS::thread_pool<BS::tp::priority> pool;  // 启用优先级
     pool.submit_task([] { std::cout << "High priority" << std::endl; }, BS::pr::high);
     pool.submit_task([] { std::cout << "Low priority" << std::endl; }, BS::pr::low);
     pool.wait();
     return 0;
 }

这些示例展示基本使用,编译运行即可上手。

应用场景

BS::thread_pool 适用于多线程场景,尤其高性能计算。以下按场景分类,附详细代码示例。

1. 科学计算与数据处理

场景:并行计算向量求和或矩阵操作,避免单线程瓶颈。

示例:并行求和 1 到 1,000,000。

 #include "BS_thread_pool.hpp"
 #include <cstdint>
 #include <iostream>
 #include <numeric>
 #include <vector>
 
 int main() {
     BS::thread_pool pool;
     using T = std::uint64_t;
     T min = 1, max = 1'000'000;
     BS::multi_future<T> mf = pool.submit_blocks(min, max + 1,
         [](T start, T end) {
             T sum = 0;
             for (T i = start; i < end; ++i) sum += i;
             return sum;
         }, 100);  // 100 块
     std::vector<T> partials = mf.get();
     T total = std::reduce(partials.begin(), partials.end());
     std::cout << total << std::endl;  // 输出 500000500000
     return 0;
 }

此例分割循环为 100 块,每块求和,返回 partials 向量。适用于大数据处理,提升多核利用率。

2. 游戏开发

场景:并行渲染帧或 AI 计算,避免主线程阻塞。

示例:并行计算平方表(模拟游戏物理计算)。

 #include "BS_thread_pool.hpp"
 #include <cstddef>
 #include <iomanip>
 #include <iostream>
 
 int main() {
     BS::thread_pool pool(10);
     constexpr std::size_t max = 100;
     std::size_t squares[max];
     pool.detach_loop(0, max, [&squares](std::size_t i) {
         squares[i] = i * i;
     });
     pool.wait();
     for (std::size_t i = 0; i < max; ++i) {
         std::cout << std::setw(2) << i << "^2 = " << std::setw(4) << squares[i] 
                   << ((i % 5 != 4) ? " | " : "
");
     }
     return 0;
 }

此例并行计算 100 个平方,适用于游戏中并行处理实体更新。

3. Web/服务器应用

场景:处理并发请求,使用暂停控制流量。

示例:启用暂停,模拟负载控制。

 #include "BS_thread_pool.hpp"
 #include <chrono>
 #include <iostream>
 #include <thread>
 
 int main() {
     BS::thread_pool<BS::tp::pause> pool;  // 启用暂停
     pool.pause();  // 初始暂停
     for (int i = 0; i < 5; ++i) {
         pool.detach_task([i] {
             std::this_thread::sleep_for(std::chrono::milliseconds(200));
             std::cout << "Task " << i << " done." << std::endl;
         });
     }
     std::cout << "Pool paused, tasks queued." << std::endl;
     pool.unpause();  // 恢复
     pool.wait();
     std::cout << "All tasks completed." << std::endl;
     return 0;
 }

暂停后任务入队但不执行,适用于服务器过载时控制。

4. 多线程输出同步

场景:日志记录,避免乱序。

示例:使用 BS::synced_stream。

 #include "BS_thread_pool.hpp"
 #include <iostream>
 
 int main() {
     BS::thread_pool pool;
     BS::synced_stream sync_out(std::cout);
     for (int i = 0; i < 10; ++i) {
         pool.detach_task([i, &sync_out] {
             sync_out.println("Task ", i, " reporting.");
         });
     }
     pool.wait();
     return 0;
 }

确保输出有序,无交错。

5. 原生扩展:优化线程亲和性

场景:高性能节点,绑定核心。

示例:设置线程亲和性(需启用宏)。

 #include "BS_thread_pool.hpp"
 #include <iostream>
 #include <vector>
 
 int main() {
     // 假设启用 BS_THREAD_POOL_NATIVE_EXTENSIONS
     std::vector<bool> affinity(4, true);  // 绑定前4核
     BS::set_os_process_affinity(affinity);
     BS::thread_pool pool(4);
     // 提交任务...
     auto opt = BS::get_os_process_affinity();
     if (opt) {
         std::cout << "Affinity set successfully." << std::endl;
     }
     return 0;
 }

适用于 NUMA 系统,提升缓存命中。

这些场景覆盖从简单到高级使用,库的灵活性使之适应各种需求。

总结

BS::thread_pool 是 C++ 多线程开发的利器,以其高效、轻量和易用脱颖而出。从介绍到特性,我们看到它如何解决线程管理痛点;架构模块化,便于扩展;快速上手门槛低;应用场景丰富,代码示例证明实则用性;社区活跃,确保持续优化。

无论科学模拟还是游戏引擎,这个库都能解锁多核潜力。推荐开发者试用,推动项目性能飞跃。未来更新将进一步融入 C++ 新标准,值得关注。

© 版权声明

相关文章

2 条评论

  • 头像
    枕樎 读者

    有用

    无记录
    回复
  • 头像
    东隅之失 读者

    收藏了,感谢分享

    无记录
    回复