为什么在std :: lock_guard之前放入std :: lock

时间:2018-04-26 14:50:35

标签: c++ multithreading c++11 mutex deadlock

继续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);
    }
};
  1. 为什么我们应用std::lock,然后将std::lock_guards应用于std::adopt_lock而不是仅仅应用2 std::lock_guards
  2. 为什么我们不能将这2 std::mutex es放在std::scoped_lock

3 个答案:

答案 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。在中,您应该使用它而不是您显示的示例代码。它被添加是因为编写该代码很糟糕。名称修改和链接问题可以简单地添加对现有锁定原语(如锁定保护)的变量支持,这就是它具有不同名称的原因。

答案 2 :(得分:3)

  1. 原因是std::lock以某种未指定的顺序锁定互斥锁,但所有线程中的顺序相同,从而保护我们免受死锁。因此,它可能是lock(lhs.m)然后是lock(rhs.m),反之亦然。这意味着我们不知道要先创建std::lock_guard中的哪一个:lhs.mrhs.m

  2. 本书似乎是以C ++ 11为基础标准编写的。 std::scoped_lock只出现在C ++ 17中。