C ++ 17引入了std::shared_mutex
类型。我一直在查看CppReference上的文档,对于产生未定义行为的案例特别感兴趣。
在阅读两种解锁方法(一种用于释放独占所有权,一种用于释放共享所有权)时,我发现文档在某种程度上有点模糊。
对于std::shared_mutex::unlock_shared
,文档说明(强调我的):
互斥锁必须由共享模式中的当前执行线程锁定,否则,行为未定义。
清楚地表明,调用unlock_shared
之前必须先调用lock_shared
,因为这是在共享模式下锁定互斥锁的唯一方法。
对于std::shared_mutex::unlock
,文档说明:
互斥锁必须由当前执行线程锁定,否则行为未定义。
在调用unlock
之前,没有提到当前执行线程必须保持的访问级别。这让我想知道它是否也能够释放共享所有权以及独家所有权。
我的问题:通过调用std::shared_mutex
代替unlock
来释放unlock_shared
的共享所有权是未定义的行为吗?
如果可能的话,我希望C ++标准中引用一个明确证实或否认上述场景中未定义行为的引用。
答案 0 :(得分:2)
我们有[thread.mutex.requirements.mutex]
表达式
m.unlock()
应格式正确,并具有以下语义:需要:调用线程应拥有互斥锁。
效果:释放调用线程对互斥锁的所有权。
返回类型:void。
同步:此操作与后续锁定操作同步,后者获取对同一对象的所有权。
引发:没什么。
因此,只要线程拥有互斥锁,无论它是否处于共享模式,unlock
都将释放互斥锁的线程所有权。
答案 1 :(得分:2)
我没有通过标准来验证其内容,但the original proposal明确指出lock
/ unlock
和lock_shared
/ {{1调用应该配对:
unlock_shared
它还明确指出存在这种分离是因为在Windows上实现SRWLOCK
的方式:
此外,某些操作系统(例如Windows)有所不同 用于解锁共享和解锁唯一的名称。用于 C ++ API中的不同名称允许更有效的绑定 这样的操作系统API。
如果标准没有明确提到这一点,那很可能是标准中的缺陷。无论这是有意的,实际上在Windows上随MSVC一起提供的所有shared_mutex | Semantics
--------------+---------------------------------------------
lock | Lock the mutex in unique ownership mode
unlock | unlock the mutex from unique ownership mode
lock_shared | Lock the mutex in shared ownership mode
unlock_shared | unlock the mutex from shared ownership mode
实现都不会像那样要求正确配对锁定解锁调用。
在POSIX系统上,shared_mutex
和unlock
确实映射到单个函数unlock_shared
,因此两者最有可能在那里相同。这是一个不应该依赖的副作用。
在Windows 7上pthread_rwlock_unlock
使用最低有效位将锁定标记为由读取器或写入器锁定。函数SRWLOCK
清除该位,破坏锁定,而unlock
仅在最后一个读取器离开锁定时清除它。以下示例显示了锁如何更改其状态:
unlock_shared
即使有另一位持有锁的读者,最终mtx.lock_shared(); // 0x11 - 1 reader, locked (shared)
mtx.lock_shared(); // 0x21 - 2 readers, locked (shared)
mtx.unlock(); // 0x20 - 2 readers, unlocked (invalid state)
mtx.lock(); // 0x21 - 2 readers, locked (shared)
也可以获取互斥锁。之后lock
被认为被2位读者锁定,因此更多读者可以进入锁定状态。因此,在上面的序列std::shared_mutex
已将互斥锁定在共享所有权模式中,而不是排他性作为误用的副作用。