从CPPReference中,没有明确说明std::mutex
如果锁不会导致死锁,则锁函数不会抛出。
PThread 的锁只有一个死锁错误。我不知道window的线程实现。我也不知道它们是否是用作std::thread
/后端的线程的其他实现std::mutex
。
所以我的问题是“我是否应该编写我的代码,就好像在某些时候,没有特殊原因,锁可能会失败?”。
我实际上需要在一些 noexcept 方法中锁定互斥锁,并且我想确保它们是 noexcept。
从CPPReference中,没有明确说明std::mutex
如果锁不会导致死锁,则锁函数不会抛出。
PThread 的锁只有一个死锁错误。我不知道window的线程实现。我也不知道它们是否是用作std::thread
/后端的线程的其他实现std::mutex
。
所以我的问题是“我是否应该编写我的代码,就好像在某些时候,没有特殊原因,锁可能会失败?”。
我实际上需要在一些 noexcept 方法中锁定互斥锁,并且我想确保它们是 noexcept。
std::mutex::lock()
成员函数未声明为c ++11 标准(草案 n3337)noexcept
第 30.4.1.2 节Mutex 类型,第 6 节:
表达式
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 节 Class mutex,第 4 节:
[ 注意:如果拥有互斥对象的线程对该对象调用 lock(),程序可能会死锁。如果实现可以检测到死锁,则可能会观察到 resource_deadlock_would_occur 错误情况。——尾注]
通过选择std::mutex
锁定类型,程序员明确声明同一线程尝试锁定mutex
它已经锁定的对象是不可能的。如果线程重新锁定 a 是合法的执行路径,mutex
则 astd:recursive_mutex
是更合适的选择(但更改为 arecursive_lock
并不意味着lock()
函数没有异常)。
在 POSIX 系统上,std::mutex
可能会使用 POSIX 互斥锁实现,并std::mutex::lock()
最终委托给pthread_mutex_lock()
. 尽管 C++ 互斥锁不需要使用 POSIX 互斥锁来实现,但 C++ 标准多线程的作者似乎已经根据 POSIX 错误条件对可能的错误条件进行了建模,因此检查这些可能是有益的。正如用户 hmjd 所说,该方法允许的 C++ 错误条件lock
是 operation_not_permitted
,resource_deadlock_would_occur
和device_or_resource_busy
.
POSIX 错误条件是:
EINVAL
: 如果一个 POSIX 特定的锁优先特性被滥用,如果你只使用标准的 C++ 多线程工具就永远不会发生这种情况。这种情况可能对应于operation_not_permitted
C++ 错误代码。EINVAL
:如果互斥体尚未初始化,这将对应于损坏的std::mutex
对象、使用悬空引用或其他一些指示程序错误的未定义行为。EAGAIN
:如果互斥体是递归的并且递归太深。这不会发生在 a 上std::mutex
,但可能发生在 astd::recursive_mutex
上。这似乎与device_or_resource_busy
错误条件相对应。EDEADLK
: 如果因为线程已经持有锁而发生死锁。这将对应于resource_deadlock_would_occur
C++ 错误代码,但表示程序错误,因为程序不应该尝试锁定std::mutex
它已经持有的锁(std::recursive_mutex
如果你真的想这样做,请使用 a)。C++operation_not_permitted
错误代码显然是为了对应 POSIXEPERM
错误状态。该pthread_mutex_lock()
函数从不提供此状态代码。但是描述该功能的 POSIX 手册页也描述了该功能,如果您尝试解锁您尚未锁定的锁pthread_mutex_unlock()
,则可能会给出该功能。EPERM
也许 C++ 标准作者operation_not_permitted
误读了 POSIX 手册页。由于 C++ 没有锁“权限”的概念,因此很难看出任何正确构造和操作的锁(根据 C++ 标准使用,不调用任何未定义的行为)如何EPERM
导致operation_not_permitted
.
device_or_resource_busy
C++17 不允许,这表明它在实践中从未真正发生过,并且它包含在 C++11 中是一个错误。
总而言之,唯一std::mutex::lock()
可能引发异常的情况表明程序错误。因此,假设方法“从不”抛出异常是合理的。
如果您可以保证不存在任何错误条件(如 hmjd 的答案中所述),则可以安全地假设互斥锁不会抛出。如何将该调用放入 noexcept 函数取决于您希望如何处理(几乎不可能的)故障。如果默认的 noexcept (to callstd::terminate
是可以接受的,你不需要做任何事情。如果你想记录不可能的错误,把函数包装在 try/catch 子句中。