死锁问题的原因是资源的相互等待,这里所说的资源通常是指锁(当然也可以是其它资源),导致两个线程都无法继续,因为每个线程都在等着对方释放资源。以下提出几种避免死锁的方案
方案1:如果在某次操作时,需要锁住两个互斥元,那么这两个互斥元要么同时锁住,要么一个都不锁. 考虑这样一种场景:需要将两个数据做一下交换,在交换时,这两个数据需要被锁住。那么在交换的代码里面同时锁住这两个数据,代码如下所示:
class some_big_object;
void swap(some_big_object& lhs, some_big_object& rhs);
class X
{
private:
some_big_object some_detail;
std::mutex m;
public:
X(some_big_object const& sd) : some_detail(ds) {}
friend void swap(X& lhs, X& rhs)
{
if(&lhs == & rhs) return ;
std::lock(lhs.m, rhs.m); //同时锁住两个互斥元
std::lock_guard<std::mutex> lock_a(lhs.m, std::adopt_lock); //沿用锁的所有权,而不是再锁一次
std::lock_guard<std::mutex> lock_b(rhs.m, std::adopt_lock);//同上
swap(lhs.some_detail, rhs.come_detail);
}
};
方案2:避免嵌套锁,如果你已经持有一个锁,就别再获取锁。如果确实是需要多个锁,那么可以像方案1一样,同时获得两个锁。
方案3:在持有锁时,避免调用用户提供的代码。这条准则其时是方案2的延续。因为你调用用户提供的代码(或者第三方接口)时,你不知道它会做什么,它可能包括获取锁在内的任何事情。如果正好它会获取锁,那么就锁就形成了嵌套,可能导致死锁。
方案四:以固定顺序获取锁。如果你确定需要获取多个锁,并且是逐个获取而非一次性获取,那么推荐的做法是以固定顺序获取锁。比如A,B,C三个互斥元,如果每次都以A->B->C的顺序获取锁,那么是不会死锁的。