【问题标题】:Why put std::lock before std::lock_guard为什么将 std::lock 放在 std::lock_guard 之前
【发布时间】:2018-10-07 08:02:30
【问题描述】:

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,然后应用2个std::lock_guardsstd::adopt_lock,而不是一个接一个地应用2个std::lock_guards
  2. 为什么我们不能把这 2 个std::mutexes 放在std::scoped_lock 中??

【问题讨论】:

  • (1) 死锁。 (2) 你说的是哪个scoped_lock?我在代码中没有看到任何scoped_lock
  • 我不是用锁的三行,为什么我们不能把 2 个 mtexes 放在 scopted 锁中
  • @RaymondChen OP 询问为什么不能使用std::scoped_lock 而不是std::lock 和两个std::lock_guard
  • 当然,如果你有一台时光机,你就可以做到。这本书写于 2012 年,但 scoped_lock 直到 2017 年才推出。
  • 您能否形成一个答案@FrançoisAndrieux,这正是我要问的。 tnx

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


【解决方案1】:

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

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

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

如果您查看您链接的文章的发布日期,c++17 还不存在。由于std::scoped_lock是c++17引入的,所以不能在文章中使用。这种锁定问题是std::scoped_lock 旨在解决的问题,应该在现代代码中使用。

【讨论】:

  • @EduardRostomyan std::lock 说:“使用死锁避免算法锁定给定的Lockable 对象lock1, lock2, ..., lockn 以避免死锁。”。标准未指定给std::lock 的互斥锁的锁定顺序,但对于给定的互斥锁集合,无论它们作为参数提供的顺序如何,都保证以一致的顺序锁定,这样这些类型的不会发生基于顺序的死锁。由实现方式决定。
  • Here is an in-depth study of how std::lock works(或应该工作)。如果您的std::lock 没有执行此文件,请报告错误。
【解决方案2】:

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

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

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

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

【讨论】:

    【解决方案3】:
    1. 原因是std::lock 以某种未指定的顺序锁定互斥锁,但所有线程中的顺序都相同,从而保护我们免受死锁。所以,它可能是lock(lhs.m),然后是lock(rhs.m),或者反过来。这意味着我们不知道首先创建std::lock_guards 中的哪一个:对于lhs.mrhs.m

    2. 看来这本书是用C++11作为基础标准写的。 std::scoped_lock 仅出现在 C++17 中。

    【讨论】:

      猜你喜欢
      • 2017-03-28
      • 1970-01-01
      • 2014-04-25
      • 1970-01-01
      • 1970-01-01
      • 2017-09-18
      • 1970-01-01
      • 1970-01-01
      • 2013-12-29
      相关资源
      最近更新 更多