<mutex> 和 <condition_variable> 的异常处理

Exception handling for <mutex> and <condition_variable>

假设

  1. 没有发生未定义的行为,
  2. 没有发生死锁,
  3. 互斥锁被正确的线程以正确的顺序锁定和解锁正确的次数,
  4. 非递归互斥锁不被多次锁定,
  5. 锁定递归互斥体不超过maximum level of ownership,
  6. 没有谓词传递给条件变量抛出,并且
  7. 只有标准库提供的时钟、时间点和持续时间与 std:: 互斥锁和条件变量一起使用

是否保证对不同类型的 std:: 互斥锁和条件变量进行操作(除了构造它们)不会抛出任何异常(尤其是类型 std::system_error)?

例如,如果是这样的方法:

void MyClass::setVariable() {
    std::lock_guard<std::mutex> const guard(m_mutex);
    m_var = 42; // m_var is of type int
    m_conditionVariable.notify_all();
}

void MyClass::waitVariable() {
    std::unique_lock<std::mutex> lock(m_mutex);
    m_conditionVariable.wait(lock, [this]() noexcept { return m_var == 42; });
}

假设 noexcept 是否安全,或者应该在调用点周围编写一些 try-catch 块?或者有什么注意事项吗?

请考虑 C++11、C++14 及更高版本中的所有类型的互斥锁和条件变量。

简答:否(抱歉)

如果底层同步对象未能执行其操作,这些操作中的任何一个都将抛出 std::system_error

这是因为同步原语的正确操作取决于:

  1. 可用的系统资源。

  2. 程序的其他部分不会使原语无效

虽然公平地说,如果 (1) 发生了,可能是时候重新设计应用程序了,或者 运行 它在负载较小的机器上。

如果出现(2),则程序在逻辑上不一致。

也就是说,

or should one write some try-catch blocks around the callsites?

也没有

您应该在以下条件下编写 try/catch 个块:

  1. 程序能够对错误情况做一些有用的事情(例如修复它或询问用户是否想再试一次)

  2. 您想向错误添加一些信息并重新抛出它以提供诊断面包屑跟踪(例如嵌套异常)

  3. 您希望记录失败并继续。

否则,c++ 异常处理的重点是让 RAII 负责资源重新获取并允许异常在调用堆栈中向上流动,直到找到要处理它的处理程序。

创建面包屑路径的示例:

void wait_for_object()
try
{
    _x.wait();  // let's say it throws a system_error on a loaded system
}
catch(...)
{
  std::throw_with_nested(std::runtime_error(__func__));
}

感谢 link T.C。如果现在我会说是 — 您的代码应该是安全的。由于将来标准 device_or_resource_busy 将被删除,并且正如问题的作者所说,这种情况不会以任何合理的方式发生,因此 lock 只有两种可能性:

(13.1) — operation_not_permitted — if the thread does not have the privilege to perform the operation.

(13.2) — resource_deadlock_would_occur — if the implementation detects that a deadlock would occur.

而这两种情况都被你的前置条件排除了。所以你的代码应该可以安全地使用 noexcept。