C++20线程池

发布时间:2026/10/4 6:19:17
C++20线程池 本实现基于 C20 标准摒弃了传统的常规标志位与易错组件。以下是核心代码段的设计动机与原理为什么使用 std::jthread 而不是 std::threadstd::jthread 提供了自动汇合和协作式中断机制。它的析构函数会自动发出停止请求并阻塞等待线程结束彻底消除了传统 std::thread 忘记调用 join() 导致程序崩溃的隐患并且代码更简洁安全性更高。这看着像GC机制自动销毁往往伴随着一些代价虽然代价很小但在性能要求极高的场景极低延迟与高频交易系统硬实时系统高频短生命周期并发任务上面会体现出来。为什么不需要自定义 bool stop 标志位工作线程直接接收 std::stop_token。析构函数中遍历调用 request_stop()或者直接依赖 jthread 的自动机制st.stop_requested() 会安全地接管停止信号。这使得中断逻辑与操作系统底层的线程机制绑定更深代码更简洁。为什么条件变量等待的判断逻辑是 st.stop_requested() || !tasks.empty()且随后判断 st.stop_requested() tasks.empty()这是为了实现优雅降级。收到停止信号后线程不会立刻暴力退出而是会继续消耗队列中残留的任务。只有当既收到停止请求且任务队列被彻底清空时线程才会 return 结束。为什么必须用 std::shared_ptr 包装 std::packaged_task这是 C 线程池最经典的坑。任务队列的类型是 std::functionvoid()C 标准要求 std::function 内部持有的对象必须是可拷贝的CopyConstructible。但 std::packaged_task 作为一个管理异步状态的类是仅可移动的Move-Only。通过 std::make_shared 将其放到堆上Lambda 表达式按值捕获这个 shared_ptr发生拷贝从而完美满足了 std::function 的编译要求。为什么使用 requires std::invocableFunc, Args...利用 C20 的 Concepts 机制进行编译期约束。如果调用者传入了参数类型不匹配的函数编译器会在 enqueue 调用处直接报错而不是在深层模板实例化时抛出几百行的乱码错误极大提升了 API 的易用性。为什么 Lambda 捕获要写成 [fstd::forwardFunc(f), ...argsstd::forwardArgs(args)...]() mutable这是 C20 引入的带初始化器的包扩展捕获Pack expansion in lambda init-capture。它结合了完美转发确保任务入队时参数的左值/右值属性被完整保留到实际执行的那一刻。mutable 关键字则是因为 std::invoke 在执行某些带有内部状态的仿函数时可能需要修改捕获进来的按值变量。为什么入队时要判断 pool[0].get_stop_token().stop_requested()防止“死后入队”。当线程池生命周期即将结束析构函数已触发时如果其他线程还在尝试往池子里塞任务这些任务将永远不会被执行。直接抛出 std::runtime_error 可以在运行时尽早暴露时序 Bug。#include iostream #include thread #include condition_variable #include mutex #include functional #include vector #include queue #includefuture #includememory #include concepts #includestdexcept class ThreadPool { public: explicit ThreadPool(size_t n std::thread::hardware_concurrency()) { pool.reserve(n); for(size_t i0;in;i){ pool.emplace_back([this](std::stop_token st){ while(!st.stop_requested()){ std::functionvoid() task; { std::unique_lockstd::mutex lock(mu); c_v.wait(lock,[this,st](){return st.stop_requested()||!tasks.empty();}); if(st.stop_requested()tasks.empty())return; taskstd::move(tasks.front()); tasks.pop(); } task(); } }); } } ~ThreadPool() { for (auto t : pool) { t.request_stop(); } c_v.notify_all(); } templatetypename Func,typename ...Args requires std::invocableFunc,Args... auto enqueue(Func f,Args ...args)-std::futurestd::invoke_result_tFunc,Args...{ using retTypestd::invoke_result_tFunc,Args...; auto taskstd::make_sharedstd::packaged_task(retType()) ([fstd::forwardFunc(f),...argsstd::forwardArgs(args)...]()mutable{ return std::invoke(f,std::move(args)...);}); std::futureretType restask-get_future(); { std::unique_lockstd::mutexlock(mu); if (!pool.empty() pool[0].get_stop_token().stop_requested()) { throw std::runtime_error(enqueue on stopped ThreadPool); } tasks.emplace([task](){(*task)();}); } c_v.notify_one(); return res; } private: std::mutex mu; std::condition_variable c_v; std::vectorstd::jthread pool; std::queuestd::functionvoid() tasks; };

关于本文作者

来自尧图内容编辑团队

尧图内容编辑团队 内容团队

尧图内容编辑团队

本文由尧图网络内容编辑团队执笔。团队由资深项目经理、前端工程师与设计师组成,所有内容均来自亲手交付的真实项目,先讲清问题、再给出可落地的解法。尧图深耕北京网站建设十年,服务过京华建材集团、智造科技等各行业客户,把一线经验沉淀为可复用的行业观察。

  • 十年建站经验,覆盖建材、制造、服务、文创等
  • 项目经理把关选题与事实准确性
  • 工程师与设计师联合撰写专业细节
  • 统一编辑规范,保证文风与排版一致
  • 每月复盘转化数据,迭代选题方向

延伸阅读

相关资讯与近期热门内容

深度阅读推荐

建站决策前值得细读的三篇

网站改版的5个关键决策
2024-08-12

网站改版的5个关键决策

什么时候该改版、改到什么程度、如何避免流量掉光,京华建材集团改版复盘给出答案。

获取专属建站方案

看完文章,把您的行业与预算告诉我们,免费获取一份量身定制的官网建设方案与报价。

立即免费咨询