线程与异常

简介: 最近遇到了一个c++线程抛出异常的问题 代码片段#include <iostream>#include <signal.h>#include <stdlib.h>#include <string>#include <thread>#include <unistd.h>#include &

最近遇到了一个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.

http://stackoverflow.com/questions/13999432/stdthread-terminate-called-without-an-active-exception-dont-want-to-joi
这位网友的代码如下:

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

目录
相关文章
|
2月前
|
安全 Java 开发者
丢失的8小时去哪里了?SimpleDateFormat线程不安全,多线程初始化异常解决方案
丢失的8小时去哪里了?SimpleDateFormat线程不安全,多线程初始化异常解决方案
52 0
|
2月前
|
运维 负载均衡 Java
nacos常见问题之单机nacos2.2.3线程池异常如何解决
Nacos是阿里云开源的服务发现和配置管理平台,用于构建动态微服务应用架构;本汇总针对Nacos在实际应用中用户常遇到的问题进行了归纳和解答,旨在帮助开发者和运维人员高效解决使用Nacos时的各类疑难杂症。
|
2月前
|
C++
C++多线程场景中的变量提前释放导致栈内存异常
C++多线程场景中的变量提前释放导致栈内存异常
35 0
|
2月前
|
Java
java 主线程破获子线程异常
java 主线程破获子线程异常
51 0
|
8月前
|
Java
[java进阶]——多线程Thread类,处理并发异常的方法(下)
[java进阶]——多线程Thread类,处理并发异常的方法
|
8月前
|
Java 调度
[java进阶]——多线程Thread类,处理并发异常的方法(上
[java进阶]——多线程Thread类,处理并发异常的方法
|
9月前
|
Java
线程池内运行的线程抛异常,线程池会怎么办
线程池内运行的线程抛异常,线程池会怎么办
|
11月前
|
Java
Java多线程:捕获线程异常
你处理过多线程中的异常吗?如何捕获多线程中发生的异常?捕获子线程的异常与捕获当前线程的异常一样简单吗?
如何处理JDK线程池内线程执行异常?讲得这么通俗,别还搞不懂
本篇 《如何处理 JDK 线程池内线程执行异常》 这篇文章适合哪些小伙伴阅读呢? 适合工作中使用线程池却不知异常的处理流程,以及不知如何正确处理抛出的异常