标准::螺纹(可拆卸)和异常安全

std::thread (detachable) and exception safety

本文关键字:异常 安全 可拆卸 螺纹 标准      更新时间:2023-10-16
使用

防护、RAII 和 try/catch 块来确保线程在所有情况下最终都会连接是一种常见的做法。但是,要分离的线程呢?

void aFunction() {
   std::thread t {someOtherFunction}; // someOtherFunction may throw!!!
   t.detach;
}

我是否应该使用一些异常处理机制来确保即使在异常的情况下也能正确分离t,或者这并不重要?

std::thread t {someOtherFunction}; // someOtherFunction may throw!!

如果someOtherFunction可以引发,则需要在该函数中处理异常。如果不是,则默认行为是在异常"转义"线程函数时调用std::terminate

std::thread对象构造和std::thread::detach本身也可以抛出,因此这些异常需要由aFunction()处理。

如果线程对象不可联接,则引发 detach() 方法。因此,如果您希望避免处理此异常,则可以添加一个测试,以查看线程是否可连接,然后再尝试分离它。

if (t.joinable())
  t.detach;

您被称为detach()的事实表明您不希望在稍后阶段"加入"(同步)线程,它基本上会自行处理。

与大多数异常处理问题一样,您将如何处理抛出的异常?如果无法处理异常,则要么不捕获它,要么捕获它,记录它并重新抛出它(或调用 std::terminate )。

如果未连接,~thread 调用 std::terminate 这一事实被认为是标准中的一个缺陷。

Scott Meyers 建议使用ThreadRAII包装器,如果它仍然可以连接,则在析构函数中加入线程:

class ThreadRAII {
public:
  ThreadRAII(std::thread&& thread): t(std::move(thread)) {}
  ~ThreadRAII() { if (t.joinable()) (t.join(); }
private:
  std::thread t;
};

如果someOtherFunction在另一个线程中引发异常,并且没有人捕获该异常,则调用std::terminate

使用 std::current_exceptionstd::rethrow_exception 捕获异常,将其传输到另一个线程并重新抛出它。 例如:

void thread_function(std::exception_ptr* exception)
try {
    throw std::runtime_error("test exception");
}
catch(std::exception& e) {
    *exception = std::current_exception();
}
int main() {
    // Thread creation.
    std::exception_ptr other_thread_exception;
    std::thread t{thread_function, &other_thread_exception};
    // Thread termination.
    t.join();
    if(other_thread_exception)
        std::rethrow_exception(other_thread_exception);
}

我是否应该使用一些异常处理机制来确保即使在异常的情况下也能正确分离t,或者这并不重要?

"即使在例外情况下也适当分离"是什么意思?你担心什么样的"不当"分离?

只需立即将其拆下,就不存在异常安全的问题:

std::thread t{something};
t.detach();

如果 t 的构造函数抛出,那么就没有东西可以分离了。如果没有,则将其分离为下一个语句,在此之前没有任何内容可以抛出。

分离

后,它不可联接,因此在离开作用域之前,不需要 RAII 或任何其他机制来确保已联接或分离。