1 问题描述

使用std::thread 创建线程, 代码逻辑如果主动throw 某些异常, 但是用户又没有捕获。这时候程序会产生coredump, 但是分析coredump, 会发现调用栈是缺失的,根本无法定位具体问题。

为了方便理解,下面给一个例子:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
// g++ -std=c++0x -g test.cpp -lpthread -o test
#include <stdexcept>
#include <thread>
void foo()
{
    throw std::runtime_error("foo");
}

int main()
{
    std::thread t(foo);
    t.join();
}

直接运行就会产生coredump, 通过gdb 分析:

$ gdb test core.1243

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
Program received signal SIGABRT, Aborted.
[Switching to Thread 0x7ffff7fd0700 (LWP 10278)]
0x000000318f036285 in raise () from /lib64/libc.so.6
Missing separate debuginfos, use: debuginfo-install glibc-2.14.90-24.fc16.9.x86_64 libgcc-4.6.3-2.fc16.x86_64 libstdc++-4.6.3-2.fc16.x86_64
(gdb) bt
#0  0x000000318f036285 in raise () from /lib64/libc.so.6
#1  0x000000318f037b9b in abort () from /lib64/libc.so.6
#2  0x00000031964bbc5d in __gnu_cxx::__verbose_terminate_handler() () from /usr/lib64/libstdc++.so.6
#3  0x00000031964b9e16 in ?? () from /usr/lib64/libstdc++.so.6
#4  0x00000031964b9e43 in std::terminate() () from /usr/lib64/libstdc++.so.6
#5  0x0000003196470b0b in ?? () from /usr/lib64/libstdc++.so.6
#6  0x000000318fc07d90 in start_thread () from /lib64/libpthread.so.0
#7  0x000000318f0f119d in clone () from /lib64/libc.so.6
(gdb)

2 Crash 原因分析

分析上面的core文件, foo()函数调用竟然丢失了! 这个问题基本上就很难定位了。

于时只能通过google搜索,找到了问题源头gcc中的std::thread问题

问题本质是:

libstdc++ 的std::thread 在调用的用户提供的run()函数时,外围包裹了try/catch, 当run()函数 throw 异常时,调用栈会被展开(退栈), 最后调用std::terminate(), 操作系统生成core文件, 就只能看到一部分的调用栈了。

最后确认下gcc 在哪个版本改掉了这个问题?
通过查看源码(位于gcc的libstdc++-v3/src/c++11/thread.cc), 直到 gcc8.1 才去除外围的try/catch。

3 总结

不推荐使用 std::thread 用于生产环境, 应该采用对应平台的线程API, 比如unix下使用pthread, win下使用_beginthreadex(); 当然如果需要做到跨平台,自己封装还是有些小繁琐。

正好很多跨平台库, 正好做了这些封装,我们可以直接拿过来用,比如 libuv 的 uv_thread_t, libzmq的zmq_threadstart()/zmq_threadclose()。

当然, 如果不想引入依赖,也可以参考这些库是怎么封装的。

参考