未捕获的异常对于主线程和另一个 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() unwind 仍然会部分发生在没有 noexcept 保证的情况下调用另一个函数,实际上抛出了未捕获的异常。
更新:感谢所有评论提供者,现在我明白了 C++ 异常对回溯不友好,而 GCC 作为 C++ 实现,可以自由选择在未捕获的异常从主线程抛出时不展开,而在从主线程抛出时展开工作线程。
更新:特别感谢 Sid S 和 Jive Dadson,我必须混淆一些概念:1)异常/错误处理; 2) runtime assertion 3) Segment fault, 2&3 类似,它们是不可恢复的错误,立即中止是唯一的选择,它们在调试器中也是回溯友好的,因为不涉及堆栈展开。它们根本不应该使用异常概念来实现。异常应该总是被捕获,让未捕获的异常离开 main() 是不推荐的用法。
最佳答案
为什么?就是那样子。从 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";
}
}
}
}
关于工作线程中的 C++ 未捕获异常,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/48535100/