6

CPPReference中,没有明确说明std::mutex如果锁不会导致死锁,则锁函数不会抛出。

PThread 的锁只有一个死锁错误。我不知道window的线程实现。我也不知道它们是否是用作std::thread/后端的线程的其他实现std::mutex

所以我的问题是“我是否应该编写我的代码,就好像在某些时候,没有特殊原因,锁可能会失败?”。

我实际上需要在一些 noexcept 方法中锁定互斥锁,并且我想确保它们是 noexcept。

4

3 回答 3

7

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::mutexresource_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()函数没有异常)。

于 2013-07-09T15:06:47.117 回答
3

在 POSIX 系统上,std::mutex可能会使用 POSIX 互斥锁实现,并std::mutex::lock()最终委托给pthread_mutex_lock(). 尽管 C++ 互斥锁不需要使用 POSIX 互斥锁来实现,但 C++ 标准多线程的作者似乎已经根据 POSIX 错误条件对可能的错误条件进行了建模,因此检查这些可能是有益的。正如用户 hmjd 所说,该方法允许的 C++ 错误条件lockoperation_not_permitted,resource_deadlock_would_occurdevice_or_resource_busy.

POSIX 错误条件是:

  • EINVAL: 如果一个 POSIX 特定的锁优先特性被滥用,如果你只使用标准的 C++ 多线程工具就永远不会发生这种情况。这种情况可能对应于operation_not_permittedC++ 错误代码。
  • EINVAL:如果互斥体尚未初始化,这将对应于损坏的std::mutex对象、使用悬空引用或其他一些指示程序错误的未定义行为。
  • EAGAIN:如果互斥体是递归的并且递归太深。这不会发生在 a 上std::mutex,但可能发生在 astd::recursive_mutex上。这似乎与device_or_resource_busy错误条件相对应。
  • EDEADLK: 如果因为线程已经持有锁而发生死锁。这将对应于resource_deadlock_would_occurC++ 错误代码,但表示程序错误,因为程序不应该尝试锁定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_busyC++17 不允许,这表明它在实践中从未真正发生过,并且它包含在 C++11 中是一个错误。

总而言之,唯一std::mutex::lock()可能引发异常的情况表明程序错误。因此,假设方法“从不”抛出异常是合理的。

于 2018-04-24T14:16:17.813 回答
0

如果您可以保证不存在任何错误条件(如 hmjd 的答案中所述),则可以安全地假设互斥锁不会抛出。如何将该调用放入 noexcept 函数取决于您希望如何处理(几乎不可能的)故障。如果默认的 noexcept (to callstd::terminate是可以接受的,你不需要做任何事情。如果你想记录不可能的错误,把函数包装在 try/catch 子句中。

于 2013-07-09T15:30:22.403 回答