关于多线程while(true)的排查方法
GDB 实战指南:如何排查多线程程序中的 while(true) 死循环
在 C/C++ 多线程服务端开发中,我们偶尔会遇到服务器 CPU 占用率异常飙高(甚至达到 100%)的情况。这往往是因为某个线程陷入了没有退出条件、且没有休眠(Sleep)机制的 while(true) 死循环中。
面对这类问题,通过在代码中到处加 printf 或 std::cout 来排查是非常低效的。今天,我们将手把手教你如何使用 GDB 这个强大的调试利器,快速揪出那个疯狂消耗 CPU 的罪魁祸首。
🎯 第一步:定位问题进程
当发现服务器卡顿时,首先使用 top 命令找出占用 CPU 异常的进程。
top -c
假设我们发现进程 my_server 的 CPU 占用高达 99.9%,并且它的进程 ID (PID) 是 12345。记下这个 PID,这是我们介入的入口。
🛠️ 第二步:使用 GDB 挂载并暂停程序
拿到 PID 后,我们需要使用 GDB “附着”(Attach)到这个正在运行的进程上。
gdb -p 12345
💡 核心提示:
当 GDB 成功 attach 到进程时,它会发送一个 SIGSTOP 信号。这意味着你的程序会被立刻暂停 (Paused)。此时,程序保留了它被按下了“暂停键”那一瞬间的所有“案发现场”。
(注意:如果你是在 GDB 内部通过 run 启动的程序,可以使用快捷键 Ctrl + C 来暂停程序进入调试交互命令行。)
🔍 第三步:查看多线程信息 (info threads)
程序暂停后,在 GDB 命令行中输入以下命令查看所有线程的状况:
(gdb) info threads
你会看到类似下面的输出:
Id Target Id Frame
1 Thread 0x7ffff7c29740 (LWP 12345) "my_server" 0x00007ffff790e3bf in epoll_wait () from /lib64/libc.so.6
2 Thread 0x7ffff7428700 (LWP 12346) "Worker_1" 0x00007ffff790e0b3 in futex_wait () from /lib64/libpthread.so.0
* 3 Thread 0x7ffff6c27700 (LWP 12347) "Worker_2" 0x00000000004015f2 in DataProcessor::processLoop() () at main.cpp:45
如何解读这些信息?
GDB 列出了当前所有的线程。我们需要特别关注 Frame (帧) 这一列的信息:
- 正常休眠的线程:通常会停留在操作系统的内核态系统调用上,比如
epoll_wait(等待网络事件)、futex_wait(等待锁/条件变量)、nanosleep(休眠)。这表明它们处于空闲等待状态,不是死循环的元凶。 - 异常忙碌的线程:看线程 3!它停留在
DataProcessor::processLoop(),这是一个纯用户态 (User Space) 的业务函数,属于我们自己写的代码,而不是系统的阻塞库函数。
🕵️♂️ 第四步:切换线程与堆栈追踪
锁定了嫌疑人(线程 3)后,我们切换到该线程,并使用 bt (backtrace) 命令查看它此时的调用栈:
(gdb) thread 3
[Switching to thread 3 (Thread 0x7ffff6c27700 (LWP 12347))]
#0 0x00000000004015f2 in DataProcessor::processLoop() () at main.cpp:45
(gdb) bt
#0 0x00000000004015f2 in DataProcessor::processLoop() (this=0x603010) at main.cpp:45
#1 0x00000000004021a8 in std::thread::_State_impl<std::thread::_Invoker<...>>::_M_run() ...
#2 0x00007ffff7bd1ea5 in start_thread () from /lib64/libpthread.so.0
#3 0x00007ffff78fb8cd in clone () from /lib64/libc.so.6
此时,你甚至可以使用 frame 0 配合 list 命令,直接在 GDB 里看案发现场的代码:
(gdb) list
40 void DataProcessor::processLoop() {
41 bool isReady = false;
42
43 // 致命错误:如果 isReady 永远为 false,这里将无限循环!
44 while (!isReady) {
45 // 没有任何 sleep 或者阻塞等待的代码
46 // 也没有更新 isReady 的逻辑
47 checkData();
48 }
49 }
📝 总结
排查多线程死循环/CPU 100% 问题的标准套路:
top找 PID。gdb -p <PID>挂载并暂停现场。info threads扫视全局。重点寻找没有卡在系统内核阻塞函数(如 futex、epoll)上,而是卡在自定义用户态函数中的线程。thread <ID>+bt精准定位到具体代码行。

浙公网安备 33010602011771号