线程与异常

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

时间: 2024-08-04 03:31:54

线程与异常的相关文章

如何捕获java线程中的逃逸的异常

在java线程中,在run方法中,我们要在run()方法中,把一切的异常有处理掉,也就try-catch掉.不能让这个线程抛出异常,因为如果我们不使用特殊的方式的话,我们是无法捕获从这个线程中逃逸的异常的.异常一旦抛出了,那么这个线程就会停止运行,但是不会影响主线程和其它的线程.因为主线程和其它的线程都不知道它抛出了异常. 线程在run方法抛出异常,没有catch 那么会有疑问,是不是在main函数里面没有catch ``` package Thread.UncaughtExceptionHan

Java并发编程示例(八):处理线程的非受检异常_java

Java语言中,把异常分为两类: 受检异常: 这类异常必须在throws子句中被显式抛出或者在方法内被捕获.例如,IOException异常或ClassNotFoundException异常.非受检异常: 这类异常不需要显式抛出或捕获.例如,NumberFormatException异常. 当一个受检异常在Thread对象的run()方法中被抛出时,我们必须捕获并处理它,因为run()方法不能抛出异常.而一个非受检异常在Thread对象的run()方法中被抛出时,默认的行为是在控制台打印出堆栈跟

java concurrent包自带线程池和队列详细讲解

Java线程池使用说明一简介线程的使用在java中占有极其重要的地位,在jdk1.4极其之前的jdk版本中,关于线程池的使用是极其简陋的.在jdk1.5之后这一情况有了很大的改观.Jdk1.5之后加入了java.util.concurrent包,这个包中主要介绍java中线程以及线程池的使用.为我们在开发中处理线程的问题提供了非常大的帮助.二:线程池线程池的作用:线程池作用就是限制系统中执行线程的数量.     根据系统的环境情况,可以自动或手动设置线程数量,达到运行的最佳效果:少了浪费了系统资

【黑马Android】(05)短信/查询和添加/内容观察者使用/子线程网络图片查看器和Handler消息处理器/html查看器/使用HttpURLConnection采用Post方式请求数据/开源项目

备份短信和添加短信 操作系统短信的uri: content://sms/ <?xml version="1.0" encoding="utf-8"?> <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.itheima28.backupsms" android:versionCode="1

Java 中常见的异常和自定义异常

常见异常:  java.lang.nullpointerexception    这个异常大家肯定都经常遇到,异常的解释是"程序遇上了空指针",简单地说就是调用了未经初始化的对象或者是不存在的对象,这个错误经常出现在创建图片,调用数组这些操作中,比如图片未经初始化,或者图片创建时的路径错误等等.对数组操作中出现空指针,很多情况下是一些刚开始学习编程的朋友常犯的错误,即把数组的初始化和数组元素的初始化混淆起来了.数组的初始化是对数组分配需要的空间,而初始化后的数组,其中的元素并没有实例化

关于线程生产者消费者的问题

问题描述 关于线程生产者消费者的问题 下面的程序运行的时候会一上来t2消费者线程抛异常Exception in thread ""t2"" java.lang.ArrayIndexOutOfBoundsException: -1因为当t2进入等待状态的时候,t4获得所有权,马上t4又进入等待状态,唤醒之前的t2当t2被唤醒再运行的时候已经是从bag.wait();的下一行运行,所以会抛异常.请问怎么才能避免呢?我的bag.notify()的位置是不是放的不对呢? p

Java线程面试题 Top 50

不管你是新程序员还是老手,你一定在面试中遇到过有关线程的问题.Java语言一个重要的特点就是内置了对并发的支持,让Java大受企业和程序员 的欢迎.大多数待遇丰厚的Java开发职位都要求开发者精通多线程技术并且有丰富的Java程序开发.调试.优化经验,所以线程相关的问题在面试中经常会 被提到. 在典型的Java面试中, 面试官会从线程的基本概念问起, 如:为什么你需要使用线程, 如何创建线程,用什么方式创建线程比较好(比如:继承thread类还是调用Runnable接口),然后逐渐问到并发问题像

Java线程并发控制基础知识

线程池 推荐用ThreadPoolExecutor的工厂构造类Executors来管理线程池,线程复用线程池开销较每次申请新线程小,具体看代码以及注释 public class TestThread { /** * 使用线程池的方式是复用线程的(推荐) * 而不使用线程池的方式是每次都要创建线程 * Executors.newCachedThreadPool(),该方法返回的线程池是没有线程上限的,可能会导致过多的内存占用 * 建议使用Executors.newFixedThreadPool(n

Java线程面试题 Top 50(转)

  不管你是新程序员还是老手,你一定在面试中遇到过有关线程的问题.Java语言一个重要的特点就是内置了对并发的支持,让Java大受企业和程序员的欢迎.大多数待遇丰厚的Java开发职位都要求开发者精通多线程技术并且有丰富的Java程序开发.调试.优化经验,所以线程相关的问题在面试中经常会被提到. 在典型的Java面试中, 面试官会从线程的基本概念问起, 如:为什么你需要使用线程, 如何创建线程,用什么方式创建线程比较好(比如:继承thread类还是调用Runnable接口),然后逐渐问到并发问题像