答案 0 :(得分:71)
scoped_lock
是lock_guard
的严格优越版本,可以同时锁定任意数量的互斥锁(使用与std::lock
相同的死锁避免算法)。在新代码中,您应该只使用scoped_lock
。
lock_guard
仍然存在的唯一原因是兼容性。它不能被删除,因为它在当前代码中使用。此外,事实证明,改变其定义(从一元到变量)是不可取的,因为这也是一种可观察的,因此也是破坏性的变化(但由于技术上的某些原因)。
答案 1 :(得分:47)
唯一且重要的区别是std::scoped_lock
有一个可变参数构造函数,它使用多个互斥锁。这允许以死锁方式锁定多个互斥锁,就像使用std::lock
一样。
{
// safely locked as if using std::lock
std::scoped_lock<std::mutex, std::mutex> lock(mutex1, mutex2);
}
以前你必须按照this answer的解释使用std::lock
安全地锁定多个互斥锁。
添加范围锁使这更容易使用并避免相关错误。您可以考虑弃用std::lock_guard
。 std::scoped_lock
的单个参数案例可以作为专业化来实现,因此您不必担心可能的性能问题。
GCC 7已经支持std::scoped_lock
here。
有关详细信息,您可能需要阅读standard paper
答案 2 :(得分:45)
最新答案,主要是针对以下问题:
您可以考虑弃用
std::lock_guard
。
对于通常只需要锁定一个互斥锁的常见情况,std::lock_guard
的API使用起来比scoped_lock
更加安全。
例如:
{
std::scoped_lock lock; // protect this block
...
}
上面的代码段可能是偶然的运行时错误,因为它会编译然后完全不执行任何操作。编码器可能意味着:
{
std::scoped_lock lock{mut}; // protect this block
...
}
现在它会锁定/解锁mut
。
如果在以上两个示例中使用了lock_guard
,则第一个示例是编译时错误而不是运行时错误,第二个示例与使用{{1 }}。
所以我的建议是使用最简单的工具来完成这项工作:
scoped_lock
,如果您需要在整个范围内精确锁定1个互斥锁。
lock_guard
,如果您需要锁定数量不完全为1的互斥体。
scoped_lock
,如果您需要在块范围内解锁(包括与unique_lock
一起使用)。
此建议是 不是暗示condition_variable
应该重新设计为不接受0个互斥量。存在有效的用例,其中希望scoped_lock
接受可变参数模板参数包,该参数包可能为空。空的情况下应该不锁定任何内容。
这就是scoped_lock
不被弃用的原因。 lock_guard
和 scoped_lock
可能是unique_lock
功能的超集,但事实是一把双刃剑。有时候,不会的类型同样重要(在这种情况下为默认构造)。
答案 3 :(得分:2)
以下是 C ++并发操作中的示例和报价:
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);
}
vs。
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);
}
std::scoped_lock
的存在意味着在c ++ 17之前您本可以使用std::lock
的大多数情况现在都可以使用std::scoped_lock
来编写,错误的可能性较小,这只能是一件好事!