以下是几种常见死锁场景的C++示例代码,包含编译运行说明和死锁原理分析,适合在Ubuntu系统下验证:
- 互斥锁顺序死锁(最常见)
代码示例
cpp
#include <iostream>
#include <thread>
#include <mutex>
std::mutex mutex1, mutex2;
void thread1() {
mutex1.lock();
std::cout << "Thread1 locked mutex1" << std::endl;
std::this_thread::sleep_for(std::chrono::seconds(1)); // 确保死锁发生
mutex2.lock(); // 阻塞在此
std::cout << "Thread1 locked mutex2" << std::endl;
mutex2.unlock();
mutex1.unlock();
}
void thread2() {
mutex2.lock();
std::cout << "Thread2 locked mutex2" << std::endl;
std::this_thread::sleep_for(std::chrono::seconds(1));
mutex1.lock(); // 阻塞在此
std::cout << "Thread2 locked mutex1" << std::endl;
mutex1.unlock();
mutex2.unlock();
}
int main() {
std::thread t1(thread1);
std::thread t2(thread2);
t1.join();
t2.join();
return 0;
}
验证方法
bash
# 编译运行
g++ -std=c++11 -pthread deadlock1.cpp -o deadlock1
./deadlock1
现象:程序卡住无输出,用top查看CPU占用率为0,gdb attach可看到两个线程都在lock()处阻塞。
- 条件变量死锁(生产者消费者问题错误实现)
代码示例
cpp
#include <iostream>
#include <thread>
#include <mutex>
#include <condition_variable>
std::mutex mtx;
std::condition_variable cv;
bool ready = false;
void consumer() {
std::unique_lock<std::mutex> lock(mtx);
while(!ready) {
cv.wait(lock); // 永久阻塞如果丢失通知
}
std::cout << "Consumed" << std::endl;
}
void producer() {
std::this_thread::sleep_for(std::chrono::seconds(2)); // 模拟延迟
{
std::lock_guard<std::mutex> lock(mtx);
ready = true;
}
// 忘记调用 cv.notify_one();
}
int main() {
std::thread t1(consumer);
std::thread t2(producer);
t1.join();
t2.join();
return 0;
}
验证方法
bash
g++ -std=c++11 -pthread deadlock2.cpp -o deadlock2
./deadlock2
现象:程序卡住,消费者线程永远等待。用gdb查看会显示consumer线程停在wait()处。
- 递归锁滥用死锁
代码示例
cpp
#include <iostream>
#include <mutex>
std::recursive_mutex rmtx;
void recursive(int n) {
rmtx.lock();
std::cout << "Locked, n=" << n << std::endl;
if(n > 0) {
recursive(n-1); // 递归调用
}
rmtx.unlock(); // 实际会少一次unlock
}
int main() {
std::thread t(recursive, 3);
t.join();
// 其他线程尝试获取锁
std::thread t2([]{
rmtx.lock(); // 阻塞在此
std::cout << "Never reached" << std::endl;
rmtx.unlock();
});
t2.join();
return 0;
}
验证方法
bash
g++ -std=c++11 -pthread deadlock3.cpp -o deadlock3
./deadlock3
现象:程序输出前3次锁记录后卡住,第二个线程无法获取锁。
- 哲学家就餐问题(经典死锁)
代码示例
cpp
#include <iostream>
#include <thread>
#include <mutex>
#include <vector>
const int N = 5;
std::mutex forks[N];
void philosopher(int id) {
int left = id;
int right = (id + 1) % N;
forks[left].lock();
std::cout << "Philosopher " << id << " took left fork" << std::endl;
std::this_thread::sleep_for(std::chrono::seconds(1));
forks[right].lock(); // 阻塞在此
std::cout << "Philosopher " << id << " took right fork" << std::endl;
// 进餐代码...
forks[right].unlock();
forks[left].unlock();
}
int main() {
std::vector<std::thread> philosophers;
for(int i = 0; i < N; ++i) {
philosophers.emplace_back(philosopher, i);
}
for(auto& t : philosophers) {
t.join();
}
return 0;
}
验证方法
bash
g++ -std=c++11 -pthread deadlock4.cpp -o deadlock4
./deadlock4
现象:每个哲学家都持有一个叉子,程序完全卡住。
死锁检测工具
gdb调试:
bash
gdb -p <PID>
thread apply all bt # 查看所有线程堆栈
valgrind检测:
bash
valgrind --tool=drd ./deadlock1
Clang静态分析:
bash
clang++ --analyze -Xanalyzer -analyzer-output=text deadlock1.cpp
解决方案原则
锁顺序:全局规定锁的获取顺序(如按地址排序)
超时机制:使用try_lock_for替代lock
RAII:用std::lock_guard/std::unique_lock管理锁生命周期
避免嵌套锁:重构代码减少锁作用域
这些示例覆盖了大多数实际开发中的死锁场景,建议在Ubuntu 20.04+环境下测试,所有代码均需要-pthread编译选项。