std :: lock_guard或std :: scoped_lock?

inf*_*inf 106 c++ multithreading locking c++17

C++ 17引入了一个名为的新锁类std::scoped_lock.

从文档来看,它看起来与现有的std::lock_guard类相似.

有什么区别,什么时候应该使用它?

Ker*_* SB 95

scoped_lock是一个严格优越的版本,lock_guard可以同时锁定任意数量的互斥锁(使用相同的死锁避免算法std::lock).在新代码中,您应该只使用scoped_lock.

唯一的原因lock_guard仍然是兼容性.它不能被删除,因为它在当前代码中使用.此外,事实证明,改变其定义(从一元到变量)是不可取的,因为这也是一种可观察的,因此也是破坏性的变化(但由于技术上的某些原因).

  • [Howard Hinnant 的回答](/sf/answers/4212097991/) 解释了为什么 `scoped_lock` 不是 `lock_guard` 的严格高级版本。保留“lock_guard”的原因不仅仅是因为兼容性! (10认同)
  • 此外,由于类模板参数推断,您甚至不必列出可锁定类型. (6认同)
  • @NicolBolas:这是真的,但这也适用于`lock_guard`.但它确实使得防护类更容易使用. (3认同)
  • scoped_lock仅限C++ 17 (3认同)
  • 由于它是 c++17,兼容性是它存在的一个特别好的理由。我也强烈不同意任何绝对主义的主张,即“您应该只使用”当墨水仍然从这个标准变干时。 (3认同)

How*_*ant 91

迟到的答案,主要是为了回应:

你可以考虑std::lock_guard弃用。

对于需要精确锁定一个互斥锁的常见情况,std::lock_guard使用比scoped_lock.

例如:

{
   std::scoped_lock lock;  // protect this block
   ...
}
Run Code Online (Sandbox Code Playgroud)

上面的代码片段很可能是一个意外的运行时错误,因为它编译后完全不执行任何操作。编码器可能的意思是:

{
   std::scoped_lock lock{mut};  // protect this block
   ...
}
Run Code Online (Sandbox Code Playgroud)

现在它锁定/解锁mut

如果lock_guard在上面的两个示例中使用,则第一个示例是编译时错误而不是运行时错误,第二个示例与使用scoped_lock.

所以我的建议是使用最简单的工具来完成这项工作:

  1. lock_guard 如果您需要为整个范围恰好锁定 1 个互斥锁。

  2. scoped_lock 如果您需要锁定多个不完全为 1 的互斥锁。

  3. unique_lock如果您需要在块的范围内解锁(包括与 a 一起使用condition_variable)。

这个建议 没有暗示scoped_lock要重新设计,以不接受0互斥。存在需要scoped_lock接受可能为空的可变参数模板参数包的有效用例。而空的情况下,应作任何锁定。

这就是为什么lock_guard不被弃用的原因。 scoped_lock 并且 unique_lock可能是 的功能的超集lock_guard,但事实是一把双刃剑。有时,类型不会做什么(在这种情况下是默认构造)同样重要。

  • 精彩的洞察力,感谢分享。:) (3认同)
  • 几年前,“scoped_lock”的作者 Mike Spertus 在会议间隙向我展示了这样一个案例。恐怕我不太记得细节,无法复制它。经过快速搜索后,我在网上和迈克的一篇论文中都没有找到示例。如果您迫切需要示例,我建议您直接联系 Mike。 (2认同)

inf*_*inf 71

唯一且重要的区别是std::scoped_lock具有多于一个互斥锁的可变构造函数.这允许以死锁方式锁定多个互斥锁,就像std::lock使用时一样.

{
    // safely locked as if using std::lock
    std::scoped_lock<std::mutex, std::mutex> lock(mutex1, mutex2);     
}
Run Code Online (Sandbox Code Playgroud)

以前你必须做一个小舞蹈,以安全的方式锁定多个互斥锁,std::lock如解释这个答案.

添加范围锁使这更容易使用并避免相关错误.您可以考虑std::lock_guard弃用.单个参数的情况std::scoped_lock可以作为专业化来实现,因此您不必担心可能的性能问题.

GCC 7已经得到了支持std::scoped_lock,可以在这里看到.

有关更多信息,您可能需要阅读标准文件

  • @Walter我做了https://stackoverflow.blog/2011/07/01/its-ok-to-ask-and-answer-your-own-questions/ (18认同)
  • 仅10分钟后回答您自己的问题.你真的不知道吗? (6认同)
  • 当我把它提交给委员会时,答案是"什么都没有".可能是某些算法的退化情况,这是正确的事情.或者可能是当他们打算锁定某些东西时,有足够的人意外地锁定任何东西是一个常见的问题.我真的不确定. (3认同)
  • @HowardHinnant:`scoped_lock lk; //锁定范围`中的所有互斥锁.LGTM. (3认同)
  • @KerrekSB:`scoped_lock lk;` 是`scoped_lock&lt;&gt; lk;` 的新简写。**没有**没有互斥锁。所以你是对的。;-) (2认同)
  • @inf 不确定他们从哪里得到的“......只要你假装你处于危险之中”,因为它的顺序相反。 (2认同)

陳 力*_*陳 力 7

这是C ++ Concurrency in Action的示例和报价:

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)

friend void swap(X& lhs, X& rhs)
{
    if (&lhs == &rhs)
        return;
    std::scoped_lock guard(lhs.m, rhs.m);
    swap(lhs.some_detail, rhs.some_detail);
}
Run Code Online (Sandbox Code Playgroud)

的存在std::scoped_lock意味着您std::lock现在可以使用编写大多数在c ++ 17之前使用过的情况std::scoped_lock,而发生错误的可能性较小,这只是一件好事!