文档目录

一、查看和控制线程

# 假设有一个死锁的多线程程序

(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 值