c++ 17引入了一个名为std::scoped_lock的新锁类。
std::scoped_lock
从文档判断,它看起来类似于已经存在的std::lock_guard类。
std::lock_guard
有什么区别,什么时候使用?
唯一重要的区别是std::scoped_lock有一个可变参数构造函数接受多个互斥量。这允许以避免死锁的方式锁定多个互斥对象,就像使用std::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_lock是lock_guard的严格高级版本,它一次锁定任意数量的互斥对象(使用与std::lock相同的死锁避免算法)。在新代码中,你应该只使用scoped_lock。
scoped_lock
lock_guard
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。
mut
如果在上面的两个例子中使用了lock_guard,那么第一个例子是编译时错误,而不是运行时错误,第二个例子与使用scoped_lock的版本具有相同的功能。
所以我的建议是使用最简单的工具:
lock_guard如果你需要为整个作用域锁定一个互斥锁。
scoped_lock如果你需要锁定一个不完全为1的互斥对象。
unique_lock如果你需要在块的范围内解锁(包括使用condition_variable)。
unique_lock
condition_variable
这个建议做 不暗示scoped_lock应该被重新设计为不接受0互斥对象。存在一些有效的用例,其中scoped_lock需要接受可变的模板参数包,这些模板参数包可能为空。空的case应该不锁定任何东西。
这就是为什么lock_guard没有被弃用。scoped_lock 而且 unique_lock可能是lock_guard功能的超集,但这是一把双刃剑。有时候,类型不会所做的事情同样重要(在这种情况下是默认构造)。