我一直在学习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
之前发生异常,这会导致未定义的行为吗?
注:我知道c++ 17引入了std::scoped_lock
,std::lock_guard
主要是为了与旧代码兼容。
第二个例子是未定义行为;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_lock
s是局部作用域,如果可以证明它是安全的,编译器可以并且将其优化。
如果编译器不能证明它是安全的,那么它通常是不安全的。