std::mutex::lock可以抛出即使一切看起来"good"?
Could std::mutex::lock throw even if everything looks "good"?
在CPPReference中,没有明确表示如果锁不会导致死锁,std::mutex
的锁函数就不会抛出。
PThread的锁只有一个死锁错误。我不知道windows的线程实现。我也不知道它们是否是用作std::thread
/std::mutex
后端的线程的其他实现。
因此,我的问题是"我是否应该在编写代码时,就好像在某些时候,由于没有特殊原因,锁可能会失败一样?"。
实际上,我需要在一些noexcept方法中锁定互斥对象,并且我想确保它们是noexcept。
std::mutex::lock()
成员函数未声明为noexcept
,且根据c++11标准(草案n3337)第6条:第30.4.1.2节的规定
表达式
m.lock()
应格式良好,并具有以下语义:
- 当需要异常时抛出:
system_error
(30.2.2)- 错误条件:
operation_not_permitted
——如果线程没有执行操作的权限resource_deadlock_would_occur
——如果实现检测到将发生死锁device_or_resource_busy
——如果互斥锁已经被锁定,并且阻塞是不可能的
这意味着任何使用mutex::lock()
的函数都不能标记为noexcept
,除非该函数本身能够处理异常并防止其向调用方传播。
我无法评论这些错误情况发生的可能性,但就std::mutex
和resource_deadlock_would_occur
(可能会抛出)而言,这表明代码中存在错误,而不是运行时失败,因为如果线程试图锁定其已拥有的std::mutex
,则可能会引发此错误。来自30.4.1.2.1类互斥,第4条:
[注意:如果拥有互斥对象的线程对该对象调用lock(),则程序可能会死锁。如果实现能够检测到死锁,则可能会出现resource_deadlock_would_ecurse错误情况。--end Note]
通过选择std::mutex
作为锁类型,程序员明确表示同一线程尝试锁定其已经锁定的mutex
是不可能的。如果线程重新锁定mutex
是合法的执行路径,则std:recursive_mutex
是更合适的选择(但更改为recursive_lock
并不意味着lock()
函数没有异常)。
在POSIX系统上,std::mutex
可能会使用POSIX互斥实现,而std::mutex::lock()
最终会委托给pthread_mutex_lock()
。尽管C++互斥体不需要使用POSIX互斥体来实现,但C++标准多线程的作者似乎已经根据POSIX错误条件对可能的错误条件进行了建模,因此检查这些条件可能很有指导意义。正如用户hmjd所说,lock
方法允许的C++错误条件是operation_not_permitted
、resource_deadlock_would_occur
和device_or_resource_busy
。
POSIX错误条件为:
EINVAL
:如果POSIX特定的锁优先级特性被误用,那么如果只使用标准的C++多线程功能,就永远不会发生这种情况。这种情况可能对应于operation_not_permitted
C++错误代码EINVAL
:如果互斥对象尚未初始化,这将对应于损坏的std::mutex
对象、悬挂引用的使用或其他指示程序错误的未定义行为EAGAIN
:如果互斥是递归的,并且递归太深。std::mutex
不会发生这种情况,但std::recursive_mutex
可能会发生这种情况。这似乎对应于device_or_resource_busy
错误条件EDEADLK
:如果由于线程已经持有锁而导致死锁。这将对应于resource_deadlock_would_occur
C++错误代码,但表示存在程序错误,因为程序不应试图锁定它已经锁定的std::mutex
(如果您真的想这样做,请使用std::recursive_mutex
)
C++operation_not_permitted
错误代码显然是为了对应POSIX EPERM
错误状态。pthread_mutex_lock()
函数从不提供此状态代码。但描述该功能的POSIX手册页面也描述了pthread_mutex_unlock()
功能,如果您试图解锁未锁定的锁,该功能可能会给出EPERM
。也许C++标准的作者由于错误地阅读了POSIX手册页面而包含了operation_not_permitted
。由于C++没有锁"权限"的概念,很难看出任何正确构建和操作的锁(根据C++标准使用,不调用任何未定义的行为)如何会导致EPERM
,从而导致operation_not_permitted
。
device_or_resource_busy
在C++17中是不允许的,这表明它在实践中从未真正发生过,并且它在C++11中的包含是一个错误。
总之,std::mutex::lock()
可能引发异常的唯一情况表明存在程序错误。因此,可以合理地假设方法"从不"抛出异常。
如果您能保证不存在任何错误条件(如hmjd的答案中所述),那么可以放心地假设互斥锁不会抛出。如何将该调用放入noexcept函数取决于您希望如何处理(非常不可能的)失败。如果noexcept(调用std::terminate
)的默认值是可以接受的,那么您不需要做任何事情。如果您想记录不可能的错误,请将函数包装在try/catch子句中。
- std::vector的包装器,使数组的结构看起来像结构的数组
- 看起来is_nothrow_constructible_v()在MSVC中被破坏了,我错了吗
- 学习多线程C++:添加线程不会使执行速度更快,即使它看起来应该
- 尽管一切看起来都很好,但值不会交换
- 自制的上衣:看起来一样,但不完全相同
- 如何使它看起来像正在下的雪
- 如何用逗号分隔输出?如何改进此代码以使其看起来更体面?
- 我的代码看起来不错,但某些输入会导致不需要的输出
- 为什么这个结构的大小是 40,而它看起来应该是 12/24?
- 我需要将阵列样式的邻接矩阵转换为矢量样式(以使其看起来更好)
- 结构化绑定:当某些内容看起来像引用并且行为类似于引用,但它不是引用时
- 如何使QTextEdit看起来被禁用
- 在正确性或良好的代码结构方面,这种动态对象创建看起来如何
- 看起来如此主要的错误.cpp:(.text.startup+0xd6):未定义对"vtable for Counter"的引用?
- C 时间测量看起来太慢了
- 我应该如何使 std::filesystem 看起来符合 Visual Studio 2015 的标准
- 写入.ini文件 - SimpleIni SetValue 尽管看起来成功了,但什么也没做
- std::string 上的 substr 无法正常工作,因为存在一些不可见但看起来像空格的字符
- 使标题看起来像它们在不同的文件夹中
- std::mutex::lock可以抛出即使一切看起来"good"?