c++ 17引入了一个新的锁类std::scoped_lock。
从文档中判断,它看起来类似于现有的std::lock_guard类。
有什么区别,什么时候使用?
c++ 17引入了一个新的锁类std::scoped_lock。
从文档中判断,它看起来类似于现有的std::lock_guard类。
有什么区别,什么时候使用?
当前回答
下面是一个引用自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);
}
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::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_lock是严格意义上的lock_guard的高级版本,它一次锁定任意数量的互斥对象(使用与std::lock相同的死锁避免算法)。在新代码中,应该只使用scoped_lock。
lock_guard仍然存在的唯一原因是为了兼容性。它不能被删除,因为它在当前代码中使用。此外,事实证明不希望改变它的定义(从一元到可变),因为这也是一个可观察到的,因此是破坏性的变化(但出于某种技术原因)。
下面是一个引用自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);
}
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的版本具有相同的功能。
所以我的建议是使用最简单的工具:
Lock_guard如果你需要在整个作用域中只锁定一个互斥锁。 Scoped_lock(如果需要锁定多个互斥对象,而不是恰好为1)。 如果需要在块范围内解锁(包括使用condition_variable),则使用Unique_lock。
这个建议并不意味着scoped_lock应该被重新设计为不接受0互斥对象。存在一些有效的用例,其中scoped_lock需要接受可变的模板参数包,这些模板参数包可能为空。空箱子不应该锁定任何东西。
这就是不弃用lock_guard的原因。Scoped_lock和unique_lock可能是lock_guard功能的超集,但这是一把双刃剑。有时,一个类型不能做什么(在本例中是默认构造)也同样重要。