std::lock_guard 还是 std::scoped_lock?
std::lock_guard or std::scoped_lock?
C++17 引入了一个名为 std::scoped_lock
的新锁 class。
从文档来看,它看起来类似于已经存在的 std::lock_guard
class。
有什么区别,我应该在什么时候使用它?
唯一且重要的区别是 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
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);
}
对比
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);
}
The existence of std::scoped_lock
means that most of the cases where you would have used std::lock
prior to c++17 can now be written using std::scoped_lock
, with less potential for mistakes, which can only be a good thing!
回答晚了,主要是为了回应:
You can consider std::lock_guard
deprecated.
对于需要恰好锁定一个互斥体的常见情况,std::lock_guard
有一个 API 比 scoped_lock
.
使用起来更安全一点
例如:
{
std::scoped_lock lock; // protect this block
...
}
上面的代码片段很可能是 运行 时的意外错误,因为它编译后完全没有执行任何操作。编码器可能意味着:
{
std::scoped_lock lock{mut}; // protect this block
...
}
现在它locks/unlocks mut
.
如果在上面的两个示例中使用 lock_guard
,则第一个示例是编译时错误而不是 运行 时错误,第二个示例与使用 scoped_lock
.
的版本
所以我的建议是使用最简单的工具来完成这项工作:
lock_guard
如果您需要为整个作用域精确锁定 1 个互斥量。
scoped_lock
如果您需要锁定多个不正好为 1 的互斥量。
unique_lock
如果您需要在方块范围内解锁(包括使用 condition_variable
)。
这个建议 不 暗示 scoped_lock
应该重新设计为不接受 0 个互斥体。存在有效的用例,其中希望 scoped_lock
接受可能为空的可变参数模板参数包。空箱应该不锁定任何东西。
这就是 lock_guard
未被弃用的原因。 scoped_lock
和 unique_lock
可能是 lock_guard
功能的超集,但这是一把双刃剑。有时,类型 不会 做什么同样重要(在这种情况下为默认构造)。
C++17 引入了一个名为 std::scoped_lock
的新锁 class。
从文档来看,它看起来类似于已经存在的 std::lock_guard
class。
有什么区别,我应该在什么时候使用它?
唯一且重要的区别是 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
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);
}
对比
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);
}
The existence of
std::scoped_lock
means that most of the cases where you would have usedstd::lock
prior to c++17 can now be written usingstd::scoped_lock
, with less potential for mistakes, which can only be a good thing!
回答晚了,主要是为了回应:
You can consider
std::lock_guard
deprecated.
对于需要恰好锁定一个互斥体的常见情况,std::lock_guard
有一个 API 比 scoped_lock
.
例如:
{
std::scoped_lock lock; // protect this block
...
}
上面的代码片段很可能是 运行 时的意外错误,因为它编译后完全没有执行任何操作。编码器可能意味着:
{
std::scoped_lock lock{mut}; // protect this block
...
}
现在它locks/unlocks mut
.
如果在上面的两个示例中使用 lock_guard
,则第一个示例是编译时错误而不是 运行 时错误,第二个示例与使用 scoped_lock
.
所以我的建议是使用最简单的工具来完成这项工作:
lock_guard
如果您需要为整个作用域精确锁定 1 个互斥量。scoped_lock
如果您需要锁定多个不正好为 1 的互斥量。unique_lock
如果您需要在方块范围内解锁(包括使用condition_variable
)。
这个建议 不 暗示 scoped_lock
应该重新设计为不接受 0 个互斥体。存在有效的用例,其中希望 scoped_lock
接受可能为空的可变参数模板参数包。空箱应该不锁定任何东西。
这就是 lock_guard
未被弃用的原因。 scoped_lock
和 unique_lock
可能是 lock_guard
功能的超集,但这是一把双刃剑。有时,类型 不会 做什么同样重要(在这种情况下为默认构造)。