std :: thread(可拆卸)和异常安全

时间:2016-02-02 10:17:39

标签: c++ multithreading c++11 exception stdthread

通常的做法是使用警卫,RAII和try / catch块来确保在所有情况下最终都会加入一个线程。但是,那些意味着分离的线程呢?

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

我是否应该使用一些异常处理机制来确保t即使在例外的情况下也能正确分离,或者它是否真的无关紧要?

3 个答案:

答案 0 :(得分:4)

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)。

答案 1 :(得分:1)

The fact that ~thread calls std::terminate if not joined is considered a flaw in the standard.

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来捕获异常,将其传输到另一个线程并重新抛出它。 E.g:

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);
}

答案 2 :(得分:0)

  

我是否应该使用一些异常处理机制来确保t即使在例外的情况下也能正确分离,或者它是否真的无关紧要?

你是什么意思“即使在例外的情况下也能正确分离”?你担心什么样的“不当”分离?

立即拆除它,并且不存在异常安全问题:

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

如果t的构造函数抛出,则无法分离。如果没有,则将其作为下一个语句分离,并且在此之前没有任何东西可以抛出。

一旦它被分离,它就不能连接,所以你不需要RAII或任何其他机制来确保它在你离开范围之前加入或分离。