【发布时间】:2021-10-26 13:55:01
【问题描述】:
我一直在学习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 仍然存在,主要是为了与旧代码兼容。
【问题讨论】:
-
我完全不明白第二种模式是如何正确的,不管有什么例外。如果
m1和m2在进入此代码时没有保留,那么adopt_lock会导致UB。如果它们被持有,那么std::lock(m1, m2)就是UB,因为你不能锁定你已经持有的互斥锁。 -
是的,很明显。 Lockguard 将在未锁定的互斥体上触发析构函数中的解锁。如果是 UB 或只是实现定义的错误,则不是 100%。 MSVC 将触发异常并随后终止,因为这就是异常在析构函数中所做的。
-
@NateEldredge
adapt_lock是一种用于锁卫的 NOOP。问题在于析构函数触发解锁。 -
@ALX23z: C++20 thread.lock.guard p4:
lock_guard(mutex_type& m, adopt_lock_t);"前提条件:调用线程拥有互斥锁m。" -
"...如果当前线程没有持有非共享锁,行为未定义..." en.cppreference.com/w/cpp/thread/lock_guard/lock_guard Pattern (2) 不分例外都是UB。