继续Concurrency In Action我已经达到了以下示例
Tha作者说,如果我们每次都以同一顺序锁定2 mutexes,那么我们保证会避免deadlocks。
从书中考虑这个例子:
class X
{
private:
some_big_object some_detail;
std::mutex m;
public:
X(some_big_object const& sd):some_detail(sd){}
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.some_detail);
}
};
std::lock
,然后将std::lock_guards
应用于std::adopt_lock
而不是仅仅应用2 std::lock_guards
? std::mutex
es放在std::scoped_lock
? 答案 0 :(得分:8)
为什么我们应用std :: lock然后用std :: adopt_lock应用2 std :: lock_guards而不是一个接一个地应用2 std :: lock_guards?
如果您使用两个std::lock_guard
而没有std::lock
,则a = b;
的锁定顺序将与b = a;
相反,其中a
和{{1}是b
s。如果一个线程尝试X
而另一个线程尝试a = b;
,则可能会死锁。第一个线程将拥有b = a;
的互斥锁上的锁并等待a
,而第二个线程将拥有b
的互斥锁上的锁并等待{{1} }的。使用b
可确保锁定顺序始终一致。
为什么我们不能把这个2 std :: mutexs放在std :: scoped_lock中?
如果查看您链接的文章的发布日期,则c ++ 17尚不存在。由于std::scoped_lock
是由c ++ 17引入的,因此无法在文章中使用它。这种锁定问题是a
设计要解决的问题,应该在现代代码中使用。
答案 1 :(得分:3)
std::lock
不是RAII。不在RAII中的互斥锁是危险和可怕的。如果抛出异常,你可以&#34;泄漏&#34;一把锁。
std::lock_guard
不支持死锁安全多重互斥锁定。但它是RAII,因此它使代码的其余部分更安全。如果你在一个地方锁定一个b,然后在另一个地方锁定b,那么你得到的代码可能会死锁(一个线程持有a并等待b,另一个线程持有b并等待a)。
std::lock
保证通过一些未指定的方式避免这种情况(可能包括锁上的全局顺序)。
std::scoped_lock
是c++17。在c++17中,您应该使用它而不是您显示的示例代码。它被添加是因为编写该代码很糟糕。名称修改和链接问题可以简单地添加对现有锁定原语(如锁定保护)的变量支持,这就是它具有不同名称的原因。
答案 2 :(得分:3)
原因是std::lock
以某种未指定的顺序锁定互斥锁,但所有线程中的顺序相同,从而保护我们免受死锁。因此,它可能是lock(lhs.m)
然后是lock(rhs.m)
,反之亦然。这意味着我们不知道要先创建std::lock_guard
中的哪一个:lhs.m
或rhs.m
。
本书似乎是以C ++ 11为基础标准编写的。 std::scoped_lock
只出现在C ++ 17中。