6

不建议使用std::thread

 2 years ago
source link: https://forrestsu.github.io/posts/cpp/not-use-std-thread/
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

不建议使用std::thread

2020年3月15日
| 字数 1746
| CPP
| 阅读 923

1 问题描述

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

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

// 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

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。

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

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

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


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK