直接用pstack 查所有线程用户态栈,卡在pthread_mutex_lock、__lll_lock_wait或futex的线程即线索源头;再用cat /proc//task//stack确认内核态是否futex_wait_queue_me;最后用gdb分析争用锁地址与持有状态。

直接看所有线程的调用栈,卡在 pthread_mutex_lock、__lll_lock_wait 或 futex 的线程就是线索源头。
怎么快速确认是哪个线程卡住了
程序“没响应”不等于主线程卡死,大概率是某个后台线程挂住了。别急着看源码,先用最轻量的方式筛出可疑线程:
-
ps -aux | grep your_app拿到 PID -
pstack <pid></pid>一次性打印所有线程用户态栈 —— 如果某几个线程都停在pthread_mutex_lock或nanosleep,优先盯它们 - 更细粒度:用
cat /proc/<pid>/task/<tid>/stack</tid></pid>看内核态栈,若出现futex_wait_queue_me,说明真正在等锁 - 注意:
pstack本质是gdb -p的封装,但更快;如果它输出空白或报错,说明符号缺失或权限不足,得切回gdb
用 GDB 进入具体线程看上下文
一旦锁定可疑线程 ID(比如 info threads 显示线程 3 和 5 都卡在 std::mutex::lock),就要进栈里看它到底想锁什么:
- 执行
thread 3切过去,再bt查调用链 - 关键不是第一行,而是找到最靠近顶部的
pthread_mutex_lock调用,用frame N跳进去,再list看源码上下文 - 用
print &mtx_a或print *(pthread_mutex_t*)0x7fffe40012a0确认多个线程争的是不是同一把锁(地址一致才构成竞争) - 如果
bt full能打出局部变量,就直接看锁变量名;否则靠地址比对,别信函数名——不同线程可能调同一个函数但锁不同对象
怎么判断是不是死锁,而不仅是单点阻塞
单个线程等锁不等于死锁。死锁必须满足循环等待:A 等 B、B 等 C、C 又等 A。需要人工拼图:
- 对每个卡住的线程,逆推它“已经拿了什么锁”:找
pthread_mutex_lock之前最近的加锁点,再看它之后有没有unlock;若没 unlock,且栈里还活着,大概率持有了 - 查
std::mutex持有状态需调试符号支持:print ((std::mutex*)0x...)->__lock,非 0 值通常是持有者 tid(glibc 实现) - 最可靠方式:用
thread apply all bt把全部栈导出到文件,逐行标出“已持 mtx1”“等待 mtx2”,画出依赖箭头 —— 出现闭环就实锤了 - 常见陷阱:线程刚 unlock 一把锁,还没来得及 lock 下一把,此时它既不算“持有”也不算“等待”,但正是这种时间窗口导致加锁顺序不一致
真正难的不是看到卡在哪一行,而是确认那个 mtx 变量在多处被不同顺序访问。符号缺失、release 编译、strip 过的二进制,会让 print &mtx 失效,只能靠地址硬推 —— 这时候,-g 编译和保留调试信息不是可选项,是底线。











