5

主线程和另一个 std::thread 的未捕获异常行为不同。

这是测试程序

#include <thread>

class XXX{
public:
  XXX(){std::fprintf(stderr, "XXX ctor\n");}
  ~XXX(){std::fprintf(stderr, "XXX dtor\n");}
};

void mytest(int i)
{
    XXX xtemp;
    throw std::runtime_error("Hello, world!");
}
int main(int argc, char *argv[])
{
    if(argc == 1) {
        mytest(0);
    }else{
        std::thread th([&]{mytest(0);});
        th.join();
    }
}

以上代码(C++11),由 GCC 5.4 编译,不带参数运行

XXX ctor
terminate called after throwing an instance of 'std::runtime_error'
   what():  Hello, world!
Aborted (core dumped)

运行 1 个参数:

XXX ctor
XXX dtor
terminate called after throwing an instance of 'std::runtime_error'
  what():  Hello, world!
Aborted (core dumped)

所以堆栈展开是在工作线程中执行的,而不是在主线程中执行的,为什么?

我问是因为我希望核心转储在两种情况下都提供有用的堆栈回溯信息(对于未捕获的异常)。

提前致谢!!!


进一步的试验表明,在线程函数体 mytest() 中添加noexcept关键字可以部分解决我的问题,因为 unwind 会失败,但这不是一个好的解决方案,因为如果 mytest() 在没有 noexcept 保证并实际抛出的情况下调用另一个函数,unwind 仍然会部分发生未捕获的异常。


更新:感谢所有评论提供者,现在我了解 C++ 异常对回溯不友好,并且 GCC 作为 C++ 实现,可以自由选择在从主线程抛出未捕获的异常时不展开,并在从工作线程时展开。


更新:特别感谢 Sid S 和 Jive Dadson,我必须混淆一些概念:1)异常/错误处理;2) 运行时断言 3) 段错误,2&3 类似,它们是不可恢复的错误,立即中止是唯一的选择,它们在调试器中也对回溯友好,因为不涉及堆栈展开。它们根本不应该使用异常概念来实现。应该始终捕获异常,不推荐使用离开 main() 的未捕获异常。

4

2 回答 2

3

为什么?就是那样子。从 c++11 开始,对处理除 main 以外的线程中抛出的异常有一些支持,但您需要检测线程以捕获异常并重新抛出它们。就是这样。

#include <thread>
#include <iostream>

class XXX {
public:
    XXX() { std::fprintf(stderr, "XXX ctor\n"); }
    ~XXX() { std::fprintf(stderr, "XXX dtor\n"); }
};

void mytest(int i)
{
    XXX xtemp;
    throw std::runtime_error("Hello, world!");
}

int main(int argc, char *argv[])
{
    std::exception_ptr exception_ptr = nullptr;
    if (argc == 1) {
        mytest(0);
    }
    else {
        std::thread th([&exception_ptr]() {
            try {
                mytest(0);
            }
            catch (...) {
                exception_ptr = std::current_exception();
            }
        });
        th.join();
        if (exception_ptr) {
            try {
                std::rethrow_exception(exception_ptr);
            }
            catch (const std::exception &ex)
            {
                std::cerr << "Thread exited with exception: " << ex.what() << "\n";
            }
        }
    }
}
于 2018-01-31T06:03:07.387 回答
2

您应该在它发生的线程中捕获异常。默认处理程序将调用terminate()它所在的任何位置,展开与否取决于实现。

于 2018-01-31T05:59:27.743 回答