一、查看和控制线程
# 假设有一个死锁的多线程程序
(gdb) info threads
Id Target Id Frame
1 Thread 0x7f1 main() at main.cpp:10
2 Thread 0x7f2 worker() at worker.cpp:23
3 Thread 0x7f3 worker() at worker.cpp:45 ← 当前线程
* 4 Thread 0x7f4 worker() at worker.cpp:23
# * 号表示当前线程(正在调试的线程)
# 切换到线程 2:
(gdb) thread 2
[Switching to thread 2]
#0 worker() at worker.cpp:23
23 lock_b.lock(); // 线程 2 正在等 lock_b
# 看所有线程的调用栈:
(gdb) thread apply all bt
Thread 1 (main):
#0 main() at main.cpp:10
Thread 2 (worker):
#0 worker() at worker.cpp:23 ← 等 lock_b
#1 std::thread::_Invoke() ...
Thread 3 (worker):
#0 worker() at worker.cpp:30 ← 持有 lock_a
#1 std::thread::_Invoke() ...
Thread 4 (worker):
#0 worker() at worker.cpp:23 ← 等 lock_a
#1 std::thread::_Invoke() ...
# 看到这个调用栈就知道死锁了:
# Thread 3 持有 lock_a,等 lock_b
# Thread 2 持有 lock_b,等 lock_a
# → 典型的 ABBA 死锁
二、调试无锁编程中的 Bug
无锁 Bug 的特点:不是每次都能复现,调试器里跑可能就不出现了。
// 一个简单的无锁计数器问题
#include <atomic>
struct Queue {
std::atomic<int> head{0};
std::atomic<int> tail{0};
int data[1024];
void push(int val) {
// ⚠️ Bug!不是原子的
int t = tail.load();
data[t] = val; // 写入数据
tail.store(t + 1); // 更新 tail
// 两个线程同时 push 时,可能覆盖彼此的 data[t]
}
};
// 用 GDB 观察这种 Bug:
// 1. 在两个线程中分别设断点到 push 的下一行
// 2. 用 thread 命令切换线程查看 tail 的值
// 3. 发现两个线程读到了同一个 t 值