如何检测死锁并快速定位死锁位置

在游戏中有时会遇到这样一种情况,某客户端发了个请求到服务端,但收不到服务端回复,看服务端的log,也没任何错误,最后调试跟踪代码,发现代码死锁了。遇到这种情况比较纠结,于是捣腾了一个自动检测死锁的功能,如果发生死锁,会马上打印堆栈信息,并终止程序,如果是在调试环境中,会自动断点到发生死锁的地方。
实现思路如下:

比如Task A已经拥有了Lock 1,并准备去获取Lock 2,此时检测一下Lock 2是否被其它Task拥有了,如果没有,那Task A就很Happy的直接获取Lock 2就行了。如果Lock 2已经被Task B拥有了,那就检测一下Task B是否在等待Lock 1,如果是的话就说明是死锁了,此时打印一下堆栈信息,如果在调试环境,就中断调试,以方便查看死锁现场,否则直接退出程序。
这样虽然上锁的效率会降低,但很快就能发现死锁。一般发布游戏到线上的时候,就把死锁检测功能去掉,也不会影响性能。
看下我的测试代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
TaskMutex mutex1;   
TaskMutex mutex2;

void m1() {
try {
mutex1.lock();
sleep(1);
mutex2.lock();
mutex2.unlock();
mutex1.unlock();
} catch (...) {
std::cout << boost::current_exception_diagnostic_information() << std::endl;
}
}

void m2() {
try {
mutex2.lock();
sleep(1);
mutex1.lock();
mutex2.unlock();
mutex1.unlock();
} catch (...) {
std::cout << boost::current_exception_diagnostic_information() << std::endl;
}
}

int main(int argc, char *argv[]) {
try {
IoScheduler scheduler(2);
scheduler.schedule(boost::bind(&m1));
scheduler.schedule(boost::bind(&m2));
scheduler.stop();
} catch (...) {
std::cout << boost::current_exception_diagnostic_information() << std::endl;
}
std::cout << "will exit.." << std::endl;
return 0;
}

结果:

1
2
3
4
5
6
7
8
9
10
11
$./god_task_mutex_dead_lock  
2013-Aug-01 09:22:46.306073 FATAL god:task_mutex god/task_mutex.cpp:56 lock Deadlock found between 0x8148ca0 and 0x8148ce0
2013-Aug-01 09:22:46.306710 FATAL : god/task_mutex.cpp:57 lock NOTREACHED
backtrace:
./god_task_mutex_dead_lock() [0x80bfa78]
./god_task_mutex_dead_lock() [0x8054d3b]
./god_task_mutex_dead_lock() [0x80c733c]
./god_task_mutex_dead_lock() [0x80cc7c3]
./god_task_mutex_dead_lock() [0x80d839d]
terminate called without an active exception
已放弃

文章目录