首页 > 解决方案 > std::lock_guard 和 std::adopt_lock 行为而不锁定互斥锁

问题描述

我一直在学习,std::lock并且std::lock_guard大多数示例都遵循以下模式:

std::lock(m1, m2);
std::lock_guard<std::mutex> guard1(m1, std::adopt_lock);
std::lock_guard<std::mutex> guard2(m2, std::adopt_lock);
//Do something here

然后我遇到了一个示例,该示例使用了您在使用时会使用的相同模式std::unique_lock,但使用的是lock_guard

std::lock_guard<std::mutex> guard1(m1, std::adopt_lock);
std::lock_guard<std::mutex> guard2(m2, std::adopt_lock);
std::lock(m1, m2);
//Do something here

我的问题是,如果您使用第二种模式并且在您到达之前发生异常,这会导致未定义的行为std::lock吗?

PS 我知道引入了 C++17 std::scoped_lockstd::lock_guard它仍然存在,主要是为了与旧代码兼容。

标签: c++c++11

解决方案


你的第二个例子是未定义的行为;adopt_lock构造函数假定已持有互斥锁。此 UB 在构造时触发,而不是在销毁时或引发异常时触发。


如果您使用unique_lock而不是scoped_lock,则它具有:

unique_lock( mutex_type& m, std::defer_lock_t t ) noexcept;

构造函数,它允许您std::lock稍作改动使用:

std::unique_lock<std::mutex> guard1(m1, std::defer_lock);
std::unique_lock<std::mutex> guard2(m2, std::defer_lock);
std::lock(guard1, guard2);

现在,unique_lock确实跟踪它是否有锁,因此可能存在内存和性能开销;如果unique_locks 在范围内是本地的,编译器可以并且将优化它,如果它可以证明它是安全的。

如果编译器不能证明它是安全的,那么它通常是不安全的。


推荐阅读