news 2026/5/26 22:24:45

C++11实现线程池:项目实现过程的报错与gdb调试

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
C++11实现线程池:项目实现过程的报错与gdb调试

一、固定线程池——FixedThreadPool

(1)没有输出func,且显示核心转储

线程池对象被销毁时,池子里的 std::thread 线程对象还处于 joinable() 状态(既没 join() 也没 detach()),C++ 标准强制调用 std::terminate() 终止程序。

日志里出现了 func,说明线程池的任务函数执行了,但线程没被正确回收。

失败原因:将任务添加到线程池,因为主线程的运行速度太快,连执行的机会都没有就结束了

改进:做了休眠,等待任务执行

gdb调试:

0 __GI_raise → 发出 SIGABRT 信号

1 __GI_abort → 终止程序

2 std::terminate() → C++ 强制终止(因为线程没 join)

3 std::thread::~thread() → 线程对象析构时发现还没 join ...

16 std::list::clear() → 你的线程组被清空了

17 pool::FixedThreadPool::StopThreadGroup() → 你的 Stop 函数

21 call_once → 确保 StopThreadGroup() 只执行一次

27 Stop() → 析构里调用 Stop()

28 ~FixedThreadPool() → 线程池析构函数

31 main() → 程序入口

pool::FixedThreadPool::StopThreadGroup() 就是StopThreadGroup 函数!

崩溃的直接原因就是:在 StopThreadGroup 里调用了 list::clear(),而此时线程还没被 join。

std::this_thread::yield():主线程让出CPU会导致偶尔打印不出来

问题根源:yield 不是用来 “等待” 的

std::this_thread::yield()的作用是让出当前的 CPU 时间片,它只会让主线程 “暂停一小会儿”,但不会等待任何条件完成。

在这个场景里:

  1. 主线程调用yield(),让出 CPU
  2. 调度器可能马上又把 CPU 分给主线程,也可能分给工作线程
  3. 如果调度器又给了主线程,主线程会立刻继续执行return 0,程序直接退出
  4. 工作线程还没来得及执行cout,程序就结束了,所以打印不出来

yield()是 “让一下”,不是 “等一下”,所以它的等待效果是不可靠的、随机的,这就是你偶尔能打印、偶尔不行的原因。

  • ac:9999先打印出来了
  • 但最后一个任务func:9999才打印出来
  • 说明主线程提前执行了cout << "ac:",但此时最后一个任务还没执行完

(2)线程池的拒绝策略

线程池满了,自己处理任务

(3)任务是无参无返回值的,如何返回其他类型

将任务加入到任务队列:

报错原因:因为std::function是可以拷贝的,所以要求内部包含的所有东西都是可以拷贝的,但是

std::packaged_task由于限制没有拷贝和赋值,支持移动,所以std::function中不能直接放std::packaged_tast,因此:

  • packaged_task放在堆上
  • 只创建一份
  • shared_ptr管理它
  • shared_ptr是可以随便拷贝的

成功获取返回值类型:

(4)#include<latch>一直标红,版本是C++20也标红

还不知道为什么?

(5)CPU利用率和运行效率对比

两核:

不使用线程池:一个CPU的利用率为100%

使用线程池:两个CPU的利用率为100%

轻任务:单线程 > 线程池 生成随机数、简单计算→ 线程池调度开销太大,反而慢

重任务:线程池 >>> 单线程 排序、复杂计算、IO→ 线程池多核并行,快几倍甚至十几倍

16核:

核数越多效率越快!

(6)为什么使用多线程申请内存时,比单线程申请内存的速度还要慢,慢在什么地方,为什么慢?提前预留空间也并没有很大优化?

内存分配是天生的 “串行” 操作,多线程争抢只会让它变慢。

核心原因:内存分配器(malloc)是 “带锁的”

我们平时用的new/malloc,底层都是调用操作系统的内存分配器,最常见的是glibcptmalloc。为了保证线程安全,它内部有一把全局锁

单线程场景

线程直接调用malloc,没有任何竞争,也没有加锁、解锁的开销。

分配器可以高效地找到合适的内存块,直接返回。

多线程场景

多个线程同时调用malloc,都要争抢这把锁。

同一时间,只有一个线程能拿到锁进行分配,其他线程都必须阻塞等待。

这种场景下,多线程分配就变成了串行排队执行,而且额外增加了大量的锁竞争、线程上下文切换开销。

提前预留空间(reserve)为什么优化不大?

提前 reserve(m) 空间,只是避免了内存拷贝,但绕不开 malloc 本身的瓶颈。 reserve 只是一次性申请一块大内存,避免了多次扩容的拷贝开销。 但在多线程下,每个 vector 调用 reserve 时,依然要去争抢全局锁,所以锁竞争的问题还是存在。 当你一次性 reserve 大块内存时,锁持有时间反而变长了,其他线程等待的时间也更长了,甚至可能比动态扩容更慢。

(7)优化:

resize:创建并初始化

  • size设为 m
  • 创建 m 个元素
  • []访问100% 安全

单线程和多线程同时用了resize预留空间但是使用的是自定义的随机生成函数且多线程用了门栓,时间对比如下:

单线程和多线程同时用了resize预留空间以及#include<random>中自带的随机生成函数且多线程用了门栓,时间对比如下:

reserve:看到的时间不准、是假的、是非法访问导致的

  • 开辟m 个空间
  • size 依然是 0
  • 不创建任何元素

正确编写后:

resize比reserve更快速

单线程和多线程同时用了reserve预留空间但是使用的是自定义的随机生成函数且多线程用了门栓,时间对比如下:

单线程和多线程同时用了reserve预留空间以及#include<random>中自带的随机生成函数且多线程用了门栓,时间对比如下:

发现造成多线程生成数据耗时如此慢的主要原因是因为随机数生成,换成系统的随机函数,更安全更高效

(8)wait和wait_for

(9)C++ 线程池的析构顺序与生命周期

二、缓存线程池——CachedTreadPool

CPU占比

不使用线程池时,CPU占比:CPU1处于100%

使用缓存式线程池时,CPU占比:

最初:提前用resize开辟了空间,用了自定义的随机生成函数

单线程和多线程同时提前用resize开辟了空间,用了自定义的随机生成函数且多线程用了门栓,时间对比如下:

第一次优化:提前用rerserve开辟空间,并且用了random库的随机生成

单线程和多线程同时提前用reserve开辟了空间,并且多线程用了random库的随机生成数和门栓,时间对比如下:

第二次优化:提前用resize开辟空间,并且用了random库的随机生成

单线程和多线程同时提前用resize开辟了空间,并且多线程用了random库的随机生成数和门栓,时间对比如下:


三、工作窃取线程池——WorkStealingPool

(1)出现了死锁

是因为在获取任务时使用了wait,导致了死锁

(2)出现了无输出的情况

Debug:

cmake -DCMAKE_BUILD_TYPE=Debug ..
make -j$(nproc)

启动调试:

gdb ./threadpool3

运行调试:

run

发现卡住不动:

中断程序:ctrl+c

查看所有线程:

info threads

查看函数掉用栈:

thread 1

bt

#6 main()程序从main函数开始运行。

#5 test2() at threadpoolTest3.cpp:97进入了test2()函数,运行到第 97 行

#4 std::latch::wait()主线程在这里卡住了!它在调用:lat_steal.wait();

#3 ~ #0 底层系统等待主线程已经被操作系统挂起不占 CPU,不执行任何代码死等

主线程在 test2 函数的第 97 行,也就是 lat_steal.wait() 处,永远等不到任务完成。

再查看一个非主线程的运行状态:

#0 futex_abstimed_wait_cancelable

#1 pthread_cond_wait_common

#2 pthread_cond_clockwait

#3 std::condition_variable::wait_until

#4 std::condition_variable::wait_for

#5 SyncQueue::Task() at SyncQueue_4.hpp:98

#6 WorkStealingPool::RunThread() at WorkStealingPool.hpp:50

  • 主线程卡死在latch.wait()→ 任务没执行完
  • 工作线程全部卡在SyncQueue::Task()→ 拿不到任务
  • 提交任务时用了默认队列容量 500,而任务数是 10000 → 队列早就塞满了,主线程在Put()时就已经阻塞,后面的任务根本没提交进去
  • 队列里的任务数为 0 → 工作线程永远拿不到任务,只能一直wait_for循环

mypool() 队列太小,装不下 10000 个任务 → 程序堵死卡死 → 无输出

mypool(10000,16) 队列够大 → 能装下所有任务 → 正常跑 → 有输出

单线程CPU利用率:

多线程CPU利用率:

最初:提前用resize开辟了空间,用了自定义的随机生成函数

单线程和多线程同时提前用resize开辟了空间,用了自定义的随机生成函数且多线程用了门栓,时间对比如下:

第一次优化:提前用rerserve开辟空间,并且用了random库的随机生成

单线程和多线程同时提前用reserve开辟了空间,并且多线程用了random库的随机生成数和门栓,时间对比如下:

第二次优化:提前用resize开辟空间,并且用了random库的随机生成

单线程和多线程同时提前用resize开辟了空间,并且多线程用了random库的随机生成数和门栓,时间对比如下:

四、ScheduleThreadPoo——定时线程池

(1)定时器出现了周期延迟

TimerManage 的 loop 线程

epoll_wait

触发定时器 A → 执行回调

触发定时器 B → 执行回调

如果 func() 执行很慢,后面的定时器,必须等它执行完 ,周期就会越来越不准、越来越延迟

  1. 定时器线程不阻塞Heavy task执行期间,Fast task仍然每秒准时执行

  2. 任务并发执行Fast task1Fast task 2几乎同时执行(时间差仅1ms)

  3. 周期性任务不受影响:所有定时器都按预期间隔触发

(2)使用map,实现1个Timer管理N个Callback

时间精度:

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/5/22 9:10:01

百度网盘提取码智能获取工具:10秒完成自动化查询的终极方案

百度网盘提取码智能获取工具&#xff1a;10秒完成自动化查询的终极方案 【免费下载链接】baidupankey 项目地址: https://gitcode.com/gh_mirrors/ba/baidupankey 还在为百度网盘分享链接的提取码而烦恼吗&#xff1f;每次遇到需要提取码的资源&#xff0c;都要在多个网…

作者头像 李华
网站建设 2026/5/26 22:24:28

第3章:嵌入模型与向量数据库——RAG的存储与检索核心

本章你将收获:主流嵌入模型(BGE、OpenAI、M3E、Jina)的深度对比与选型指南;向量数据库(Chroma、Qdrant、Milvus、Pinecone)的架构与适用场景;混合检索(BM25 + 向量)的实现方法;重排序(Reranker)如何提升Top-5准确率;以及为100万文档构建高效检索系统的完整实战。 …

作者头像 李华
网站建设 2026/5/22 9:07:00

回归更新,一个简单的重新认识

【回归更新】今日已提交CSDN官方企业认证&#xff0c;全栈编程内容持续输出各位CSDN的朋友们&#xff0c;大家好&#xff01;我是你们的老朋友&#xff0c;之前呢以DuLuo形式存在&#xff0c;现在改名换新啦&#xff0c;叫渡落居&#xff0c;也是一名深耕编程与实战开发的技术博…

作者头像 李华
网站建设 2026/5/22 9:06:06

BarrageGrab:零依赖微服务架构的跨平台直播弹幕一体化采集系统

BarrageGrab&#xff1a;零依赖微服务架构的跨平台直播弹幕一体化采集系统 【免费下载链接】BarrageGrab 抖音快手bilibili直播弹幕wss直连&#xff0c;非系统代理方式&#xff0c;无需多开浏览器窗口 项目地址: https://gitcode.com/gh_mirrors/ba/BarrageGrab 在直播电…

作者头像 李华
网站建设 2026/5/22 9:05:29

C++ 第十三章第十四章 案例教程

C++ 第十三章&第十四章 案例教程 说明:本教程承接前十二章。第十三章聚焦STL进阶(容器适配器、优先级队列、算法复杂度、仿函数、无序容器),第十四章讲解多线程编程(thread、mutex、async、future、atomic)。通过“任务调度系统”和“并行计算器”实战串联所有知识点…

作者头像 李华
网站建设 2026/5/22 9:03:13

G-Helper:华硕笔记本的轻量级性能管家,让你的电脑重获新生

G-Helper&#xff1a;华硕笔记本的轻量级性能管家&#xff0c;让你的电脑重获新生 【免费下载链接】g-helper Lightweight Armoury Crate alternative for Asus laptops with nearly the same functionality. Works with ROG Zephyrus, Flow, TUF, Strix, Scar, ProArt, Vivobo…

作者头像 李华