Std::lock_guard或Std::scoped_lock?

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

从文档判断,它看起来类似于已经存在的std::lock_guard类。

有什么区别,什么时候使用?

80199 次浏览

唯一重要的区别是std::scoped_lock有一个可变参数构造函数接受多个互斥量。这允许以避免死锁的方式锁定多个互斥对象,就像使用std::lock一样。

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

在此之前,您必须使用std::lock以安全的方式锁定多个互斥对象,如这个答案所述。

范围锁的添加使它更容易使用,并避免了相关的错误。你可以认为std::lock_guard已弃用。std::scoped_lock的单参数情况可以实现为专门化,这样你就不必担心可能的性能问题。

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

要了解更多信息,你可能想要阅读标准的纸

scoped_locklock_guard的严格高级版本,它一次锁定任意数量的互斥对象(使用与std::lock相同的死锁避免算法)。在新代码中,你应该只使用scoped_lock

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

下面是一个来自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来编写,错误的可能性更小,这只能是一件好事!

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

你可以认为std::lock_guard已弃用。

对于需要锁定一个互斥量的常见情况,std::lock_guard有一个比scoped_lock更安全的API。

例如:

{
std::scoped_lock lock;  // protect this block
...
}

上面的代码片段很可能是一个意外的运行时错误,因为它编译后什么也不做。编码员的意思可能是:

{
std::scoped_lock lock{mut};  // protect this block
...
}

现在锁定/解锁mut

如果在上面的两个例子中使用了lock_guard,那么第一个例子是编译时错误,而不是运行时错误,第二个例子与使用scoped_lock的版本具有相同的功能。

所以我的建议是使用最简单的工具:

  1. lock_guard如果你需要为整个作用域锁定一个互斥锁。

  2. scoped_lock如果你需要锁定一个不完全为1的互斥对象。

  3. unique_lock如果你需要在块的范围内解锁(包括使用condition_variable)。

这个建议 暗示scoped_lock应该被重新设计为不接受0互斥对象。存在一些有效的用例,其中scoped_lock需要接受可变的模板参数包,这些模板参数包可能为空。空的case应该锁定任何东西。

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