问题 和的异常处理


假设

  1. 没有未定义的行为,
  2. 没有死锁,
  3. 通过正确的线程以正确的顺序锁定和解锁互斥锁正确的次数,
  4. 非递归互斥锁未被多次锁定,
  5. 锁定递归互斥锁不会超过 最高所有权
  6. 没有谓词传递给条件变量throw,和
  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 或者应该在callites周围写一些try-catch块?或者有任何警告吗?

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


9004
2018-05-13 08:51


起源

你可以通过linux上的futex()实现来查找它失败的条件: github.com/torvalds/linux/blob/master/kernel/futex.c#L3147 - Arvid
std::condition_variable::wait() 改为 noexcept 在C ++中14。它现在只是打电话 std::terminate() 当重新获取锁定失败时。你可能想要考虑一下。 - TFM
@TFM我不同意。你能引用一些文件吗? - Richard Hodges


答案:


感谢 链接 T.C。现在提供我会说是 - 您的代码应该是安全的。因为在未来的标准 device_or_resource_busy 将被删除,并且由于该问题的作者说这种情况不能以任何合理的方式发生,那么只有2种可能性 lock 投掷:

(13.1) - operation_not_permitted - 如果线程没有   特权执行操作。

(13.2) - resource_deadlock_would_occur - 如果实现检测到   会发生僵局。

并且这两种情况都被您的前提条件排除在外。所以你的代码应该安全使用noexcept。


2
2018-05-13 09:20



你能提供一个真实的例子,说明为什么或在哪里 device_or_resource_busy 在讨论的行动中可能会被抛出? - jotik
@jotik,我不能但并不意味着它是不可能的。 - ixSci
cplusplus.github.io/LWG/lwg-active.html#2309 - T.C.
@jotik,更新了答案 - ixSci


简答:不(对不起)

任何这些操作都会抛出 std::system_error 如果基础同步对象无法执行其操作。

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

  1. 可用的系统资源。

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

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

如果(2)发生,程序在逻辑上不一致。

话虽如此,

或者应该在callites周围写一些try-catch块?

也没有。

您应该在以下条件下编写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__));
}

9
2018-05-13 09:12



我认为(2)已经被我的假设1和3考虑过了。但是你能给出一个真实的例子,说明为什么(1)可能发生,即系统资源耗尽? - jotik
@jotik我可以看到你的想法。基本上你要问“在任何情况下我都可以忽略系统同步功能的返回码”吗?实际上,几乎每个人几乎都在做,因为他们失败的环境在实践中非常罕见。尽管如此,标准规定这些函数可能会抛出异常,因此库实现者可能会因为他们最了解的原因而抛出异常。因此,我们必须假设可能存在例外。 - Richard Hodges
@jotik这并不是说每个例外都需要处理。如果它非常罕见,并且只会在加载的系统上发生(程序的其他部分可能会失败),在main(或thread_main)中捕获异常,报告它并中止几乎总是合理的。 - Richard Hodges
也许它更好用 noexcept 然后让 std::terminate() 要么 std::unexpected() 报告这些而不是允许这些传播到 main()?这可能会丢失一些上下文,但可以编写更有效的代码。 - jotik
@TFM刚刚在这里检查了最新的标准草案§30.5.1 open-std.org/JTC1/SC22/WG21/docs/papers/2015/n4527.pdf 你是对的 wait() 没有谓词。 wait(cv, pred) 如果谓词抛出则可以抛出。 - Richard Hodges