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

Edu*_*yan 7 c++ multithreading mutex deadlock c++11

继续使用Concurrency In Action我已经达到了以下示例.
Tha作者说,如果我们每次都以相同的顺序锁定2个互斥锁,那么我们可以保证避免死锁.
从书中考虑这个例子:

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);
    }
};
Run Code Online (Sandbox Code Playgroud)
  1. 我们为什么要应用std::lock,然后应用2 std::lock_guardsstd::adopt_lock,而不是仅仅使用2 std::lock_guards此起彼伏?
  2. 为什么我们不能把这个2 std::mutexes放在std::scoped_lock??

Yak*_*ont 9

std::lock不是 RAII。不在 RAII 中的互斥锁是危险且可怕的。如果抛出异常,您可能会“泄漏”锁。

std::lock_guard不支持死锁安全的多重互斥锁。但它是 RAII,因此它使其余代码更安全。如果您在一个位置锁定 a,然后锁定 b,然后在另一个位置锁定 b,然后锁定 a,则会得到可能发生死锁的代码(一个线程持有 a 并等待 b,另一个线程持有 b 并等待 a)。

std::lock保证通过某种未指定的方式(可能包括锁的全局顺序)来避免这种情况。

std::scoped_lock。在中,您应该使用它来代替您显示的示例代码。添加它是因为编写该代码很糟糕。名称修改和链接问题阻止了简单地向现有锁定原语(例如锁保护)添加可变支持,这就是它具有不同名称的原因。


Fra*_*eux 8

为什么我们应用std :: lock然后用std :: adopt_lock应用2 std :: lock_guards而不是一个接一个地应用2 std :: lock_guards?

如果你使用了两个std::lock_guard没有std::lock锁定顺序,a = b;则会与s 相反b = a;,其中abXs.如果一个线程尝试a = b;而另一个线程尝试b = a;它们可能会死锁.第一个线程将拥有锁定a的互斥锁并等待,b而第二个线程将拥有锁定b的互斥锁并等待a.使用std::lock确保锁定顺序始终一致.

为什么我们不能把这个2 std :: mutexs放在std :: scoped_lock中?

如果查看您链接的文章的发布日期,则c ++ 17尚不存在.由于std::scoped_lock是由c ++ 17引入的,因此无法在文章中使用它.这种锁定问题是std::scoped_lock要解决的设计,应该在现代代码中使用.

  • [这是对std :: lock`如何工作的深入研究](http://howardhinnant.github.io/dining_philosophers.html)(或_should_ work).如果你的`std :: lock`没有在这个文件中做错误报告. (5认同)
  • @EduardRostomyan`std :: lock`说:*"锁定给定的[`Lockable`](http://en.cppreference.com/w/cpp/concept/Lockable)对象`lock1,lock2,...,lockn `使用死锁避免算法来避免死锁."*.赋予`std :: lock`的互斥锁被锁定的顺序不是由标准指定的,但是它保证以给定集合互斥锁的一致顺序锁定,无论它们作为参数提供的顺序如此,这些基于订单的死锁不会发生.这取决于它是如何完成的. (4认同)