17.8.6 死锁
一个单独的互斥量是不会引起问题的。然而当你同时持有多个互斥量时,各种奇怪的情况就可能发生了。来看一下列表 17-14 这个例子。
Listing 17-14.deadlock_ex
mutex A, B;
thread1 () {
lock(A);
lock(B);
unlock(B);
unlock(A);
}
thread2() {
lock(B);
lock(A);
unlock(A);
unlock(B);
}
这段伪代码演示了两个线程一起 hang 住并永远 hang 住的场景。想像一下,因为不幸地调度而产生的如下执行序列:
线程 1 锁住 A; 控制权切换到线程 2。
线程 2 锁住 B; 控制权切换到线程 1。
之后线程尝试:
线程 1 尝试锁 B,但 B 已经被线程 2 锁住了。
线程 2 尝试锁 A,但 A 已经被线程 1 锁住了。
两个线程会永远陷入这种状态。这种线程陷入持有锁且等待其它线程解锁的状态被称为死锁。
引起死锁的原因是不同的线程按照不同的顺序获取锁。所以我们也可以得出一条简单的规则来避免死锁,按顺序对资源上锁就能帮我们节省大量的时间了。
■预防死锁 将你程序中的所有互斥量进行假想排序。只按照这种假想的顺序来对互斥量加锁。
例如,假设我们有互斥量 A,B,C 和 D。将其以 A < B < C < D 的顺序排好序,如果你想要对 D 和 B 同时加锁,那么你就需要按照上述相同的顺序来加锁,也就是说先锁 B,再锁 D。如果这种规则能够保证执行的话,不会有任何两个线程陷入死锁状态。
■Question 362 什么是 Coffman’s conditions? 如何用其来诊断死锁?
■Question 363 如何使用 Helgrind 来检测死锁?