最近遇到了一个c++线程抛出异常的问题
代码片段
#include <iostream>
#include <signal.h>
#include <stdlib.h>
#include <string>
#include <thread>
#include <unistd.h>
#include <vector>
using namespace std;
vector<thread*> v1;
vector<thread> v2;
void task1(std::string msg){
while (1) {
cout << "task1 says: " << msg << endl;
sleep(2);
}
}
void f(int s)
{
cout << "ctrl-c\n";
exit(0);
}
void t1()
{
for (int i=0; i<3; i++) {
v1.push_back(new thread(task1, "hello"));
}
for (int i=0; i<3; i++) {
v1[i]->join();
}
}
void t2()
{
for (int i=0; i<3; i++) {
v2.push_back(thread(task1, "hello"));
}
for (int i=0; i<3; i++) {
v2[i].join();
}
}
int main() {
signal(SIGINT,f);
//t1();
t2();
return 0;
}
以上代码,单独执行t1,正常;单独执行t2,期间ctrl+c停止时,就会抛出异常:
terminate called without an active exception
Aborted (core dumped)
但如果把vector v2放进main中,就没问题了;或者去掉SIGINT信号的捕获,也正常;
这个情况,该如何解释呢?
gdb 结果
(gdb) bt
#0 0x0000003a47432625 in raise () from /lib64/libc.so.6
#1 0x0000003a47433e05 in abort () from /lib64/libc.so.6
#2 0x0000003a4a46007d in __gnu_cxx::__verbose_terminate_handler () at ../../.././libstdc++-v3/libsupc++/vterminate.cc:95
#3 0x0000003a4a45e0e6 in __cxxabiv1::__terminate (handler=<optimized out>) at ../../.././libstdc++-v3/libsupc++/eh_terminate.cc:47
#4 0x0000003a4a45e131 in std::terminate () at ../../.././libstdc++-v3/libsupc++/eh_terminate.cc:57
#5 0x000000000040172f in std::thread::~thread() ()
#6 0x00000000004036ad in void std::_Destroy<std::thread>(std::thread*) ()
#7 0x0000000000403396 in void std::_Destroy_aux<false>::__destroy<std::thread*>(std::thread*, std::thread*) ()
#8 0x000000000040311c in void std::_Destroy<std::thread*>(std::thread*, std::thread*) ()
#9 0x0000000000402dd6 in void std::_Destroy<std::thread*, std::thread>(std::thread*, std::thread*, std::allocator<std::thread>&) ()
#10 0x000000000040415b in std::vector<std::thread, std::allocator<std::thread> >::~vector() ()
#11 0x0000003a47435b22 in exit () from /lib64/libc.so.6
#12 0x000000000040142b in f(int) ()
#13 <signal handler called>
#14 0x0000003a478082fb in pthread_join () from /lib64/libpthread.so.0
#15 0x0000003a4a4bb627 in __gthread_join (__value_ptr=0x0, __threadid=<optimized out>)
at /root/tmp/gcc-4.9.3/x86_64-unknown-linux-gnu/libstdc++-v3/include/x86_64-unknown-linux-gnu/bits/gthr-default.h:668
#16 std::thread::join (this=<optimized out>) at ../../../.././libstdc++-v3/src/c++11/thread.cc:107
#17 0x0000000000401540 in t2() ()
#18 0x0000000000401585 in main ()
首先v2是全局变量,按ctrl+c就会执行exit,v2要析构,就导致thread对象要析构,但此时它们都是joinable状态,析构函数会调用terminate
把v2作为局部变量就没问题了,不会导致vector析构被调用
关于joinable
http://en.cppreference.com/w/cpp/thread/thread/joinable
Checks if the thread object identifies an active thread of execution. Specifically, returns true if get_id() != std::thread::id(). So a default constructed thread is not joinable.
A thread that has finished executing code, but has not yet been joined is still considered an active thread of execution and is therefore joinable. 【已经完成了,但还没有被joined,则仍是一个执行线程,仍是joinable】
测试代码:
#include <iostream>
#include <thread>
#include <chrono>
void foo()
{
std::this_thread::sleep_for(std::chrono::seconds(1));
}
int main()
{
std::thread t;
std::cout << "before starting, joinable: " << t.joinable() << '\n';//0
t = std::thread(foo);
std::cout << "after starting, joinable: " << t.joinable() << '\n';//1
t.join();
std::cout << "after joining, joinable: " << t.joinable() << '\n';//0
}
关于线程的析构函数
http://en.cppreference.com/w/cpp/thread/thread/~thread
要点:
- 当前线程实例有关联的线程,也就是joinable,则析构调用terminate
- 当前线程实例在以下情况下没有关联的线程(都不是joinable)
- 刚刚创建
- 被moved
- join已经调用
- detach已经调用
来自stackoverflow网友
The destructor for std::thread will call std::terminate if it is run on a thread if you not have called join() (to wait the thread to finish) or detach() (to detach the thread from the object) on it. Thus the programmer must ensure that the destructor is never executed while the thread is still joinable
When a thread object goes out of scope and it is in joinable state, the program is terminated. The Standard Committee had two other options for the destructor of a joinable thread. It could quietly join – but join might never return if the thread is stuck. Or it could detach the thread (a detached thread is not joinable). However, detached threads are very tricky, since they might survive till the end of the program and mess up the release of resources. So if you don’t want to terminate your program, make sure you join (or detach) every thread.
Once a thread has been started within a scope (which itself is running on a thread), one must explicitly ensure one of the following happens before the thread goes out of scope:
- The runtime exits the scope, only after that thread finishes executing. This is achieved by joining with that thread. Note the language, it is the outer scope that joins with that thread.
- The runtime leaves the thread to run on its own. So, the program will exit the scope, whether this thread finished executing or not. This thread executes and exits by itself. This is achieved by detaching the thread. This could lead to issues, for example, if the thread refers to variables in that outer scope.
Note, by the time the thread is joined with or detached, it may have well finished executing. Still either of the two operations must be performed explicitly.
void userStop(bool *st)
{
char chChar = getchar();
if(chChar == '\n') {
*st = true;
}
}
void func3()
{
bool stopper = false;
thread stopThread(userStop, &stopper); // start thread looking for user input
for(int i = 0; i < 1000; i++) {
if(stopper) { break; } // break if desired
// Do stuff
//sleep(2);
}
}
问题在于stopThread离开作用域导致析构,而此时它仍是joinable(在等待用户输入)
有用的链接
http://stackoverflow.com/questions/7381757/c-terminate-called-without-an-active-exception