排查死锁(deadlock)是多线程 C++ 开发中的一项核心调试技能,死锁通常是因为多个线程交叉持有资源而相互等待导致程序卡死。下面详细讲讲如何排查和预防死锁:
在加锁和解锁前后打日志,确认:
std::mutex m1, m2;
void funcA() {
std::cout << "Thread A trying m1\n";
std::lock_guard<std::mutex> lock1(m1);
std::cout << "Thread A locked m1\n";
std::this_thread::sleep_for(std::chrono::milliseconds(100));
std::cout << "Thread A trying m2\n";
std::lock_guard<std::mutex> lock2(m2); // ⚠️ 死锁风险
std::cout << "Thread A locked m2\n";
}
如果卡在 trying m2
说明死锁了。
编译加 -g
,使用 gdb
:
gdb ./your_program
(gdb) run
# 当卡住时 Ctrl+C 停止程序
(gdb) info threads
(gdb) thread apply all bt
查看每个线程的栈,可以看到卡在 __lll_lock_wait
或 pthread_mutex_lock
的线程说明在等锁。
gdb
(最通用)
Valgrind
的 helgrind
模式:检查数据竞争和死锁
valgrind --tool=helgrind ./your_program
例如 Boost 的 boost::mutex
配合调试工具能发现潜在死锁。
原则 | 说明 |
---|---|
统一加锁顺序 | 所有线程按相同顺序获取多个锁 |
使用 std::lock() |
原子性地获取多个锁,避免交叉等待 |
使用 std::unique_lock |
配合条件变量,灵活可释放 |
尽量缩小锁粒度 | 避免锁住耗时操作 |
RAII 自动释放锁 | 使用 lock_guard 或 unique_lock |
使用 try_lock 检测环形依赖 |
可编写检测逻辑,避免死锁 |
std::mutex m1, m2;
void safe_func() {
std::unique_lock<std::mutex> lock1(m1, std::defer_lock);
std::unique_lock<std::mutex> lock2(m2, std::defer_lock);
std::lock(lock1, lock2); // 原子加锁,避免死锁
}
如你愿意,我可以:
需要哪种帮助?