如果在生成 std​​::thread 后引发异常,则不会捕获异常

Posted

技术标签:

【中文标题】如果在生成 std​​::thread 后引发异常,则不会捕获异常【英文标题】:Exception not caught if raised after spawning std::thread 【发布时间】:2017-08-20 14:21:59 【问题描述】:

我对异常的奇怪行为感到困惑,这些异常是在生成另一个线程后在主线程中抛出的:

void thread_body()
    while(true) cout << "in thread" << endl;


int main(int argc, char** argv)

    try
        auto t = std::thread( thread_body );

        throw std::runtime_error("error!");

        t.join();
     catch (const std::exception& e) 
        cout << e.what() << endl;
    

输出是:

in thread
in thread
in thread
terminate called without an active exception
The program has unexpectedly finished.

如果我在生成这样的线程之前抛出:

throw std::runtime_error("error!");
auto t = std::thread( thread_body );

比正常抓到:

错误!

为什么在第一种情况下没有捕获到异常?我应该怎么做才能以通常的方式捕捉它?

【问题讨论】:

【参考方案1】:

当抛出异常时,线程对象将被销毁。但是线程析构函数将在它仍然可以连接时被调用。这会导致调用terminate,因此永远不会调用异常处理程序。

在没有适当同步的情况下从不同线程写入标准流也不是一个好主意。

【讨论】:

“在没有适当同步的情况下从不同线程写入标准流不是一个好主意。” - 这不是一个好主意,但标准确实允许多个线程在没有显式同步的情况下写入 cout 等。 标准只要求不出现竞争条件。但是产生的输出可能会搞砸。 好的,但这意味着在生成任何其他线程之后在主线程中抛出的任何异常都无法被外部 catch 块捕获!这很奇怪,也没有什么意义,因为我需要在创建任何线程后插入单独的 try 块。我猜应该有一些解决方案。【参考方案2】:

好的,经过一番研究,我找到了这个问题的解决方案。必须将线程包装到类中并创建其实例,而不是创建原始线程。在析构函数中,可以检查线程是否仍可连接,并在可能的情况下执行操作以优雅地停止线程主体。在这种情况下,当抛出异常时,将在线程仍在运行时调用析构函数,并对其进行整理。

【讨论】:

以上是关于如果在生成 std​​::thread 后引发异常,则不会捕获异常的主要内容,如果未能解决你的问题,请参考以下文章

OpenCV 分配导致 std::thread::join 中的段错误

std::thread 在 vc++ 的析构函数中获取中止异常

std::thread,在线程中抛出异常会导致 Visual C++ 中的中止错误

使用参数创建时,std::thread 抛出访问冲突异常? [关闭]

boost::thread interrupt() 仅在第一次执行中断点时引发异常?

std::thread 在无限循环中随机时间后锁定