<mutex> 和 <condition_variable> 的异常处理
Exception handling for <mutex> and <condition_variable>
假设
- 没有发生未定义的行为,
- 没有发生死锁,
- 互斥锁被正确的线程以正确的顺序锁定和解锁正确的次数,
- 非递归互斥锁不被多次锁定,
- 锁定递归互斥体不超过maximum level of ownership,
- 没有谓词传递给条件变量抛出,并且
- 只有标准库提供的时钟、时间点和持续时间与
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),则程序在逻辑上不一致。
也就是说,
or should one write some try-catch blocks around the callsites?
也没有
您应该在以下条件下编写 try/catch 个块:
程序能够对错误情况做一些有用的事情(例如修复它或询问用户是否想再试一次)
您想向错误添加一些信息并重新抛出它以提供诊断面包屑跟踪(例如嵌套异常)
您希望记录失败并继续。
否则,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。
假设
- 没有发生未定义的行为,
- 没有发生死锁,
- 互斥锁被正确的线程以正确的顺序锁定和解锁正确的次数,
- 非递归互斥锁不被多次锁定,
- 锁定递归互斥体不超过maximum level of ownership,
- 没有谓词传递给条件变量抛出,并且
- 只有标准库提供的时钟、时间点和持续时间与
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),则程序在逻辑上不一致。
也就是说,
or should one write some try-catch blocks around the callsites?
也没有
您应该在以下条件下编写 try/catch 个块:
程序能够对错误情况做一些有用的事情(例如修复它或询问用户是否想再试一次)
您想向错误添加一些信息并重新抛出它以提供诊断面包屑跟踪(例如嵌套异常)
您希望记录失败并继续。
否则,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。