线程与异常

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

目录
相关文章
|
6月前
|
安全 Java 开发者
丢失的8小时去哪里了?SimpleDateFormat线程不安全,多线程初始化异常解决方案
丢失的8小时去哪里了?SimpleDateFormat线程不安全,多线程初始化异常解决方案
93 0
|
27天前
线程CPU异常定位分析
【10月更文挑战第3天】 开发过程中会出现一些CPU异常升高的问题,想要定位到具体的位置就需要一系列的分析,记录一些分析手段。
50 0
|
15天前
|
监控 Java
在实际应用中选择线程异常捕获方法的考量
【10月更文挑战第15天】选择最适合的线程异常捕获方法需要综合考虑多种因素。没有一种方法是绝对最优的,需要根据具体情况进行权衡和选择。在实际应用中,还需要不断地实践和总结经验,以提高异常处理的效果和程序的稳定性。
17 3
|
15天前
|
监控 Java
捕获线程执行异常的多种方法
【10月更文挑战第15天】捕获线程执行异常的方法多种多样,每种方法都有其特点和适用场景。在实际开发中,需要根据具体情况选择合适的方法或结合多种方法来实现全面有效的线程异常捕获。这有助于提高程序的健壮性和稳定性,减少因线程异常带来的潜在风险。
11 1
|
15天前
|
监控 API
Hook 线程与捕获线程执行异常
【10月更文挑战第11天】Hook 线程和捕获线程执行异常是多线程编程中不可或缺的技术。通过深入理解和掌握这些方法,我们可以提高程序的稳定性和可靠性,更好地应对各种异常情况。同时,在实际应用中要注意平衡性能和准确性,制定合理的异常处理策略,以确保程序的正常运行。
26 1
|
2月前
|
消息中间件 前端开发 NoSQL
面试官:线程池遇到未处理的异常会崩溃吗?
面试官:线程池遇到未处理的异常会崩溃吗?
70 3
面试官:线程池遇到未处理的异常会崩溃吗?
|
2月前
|
监控 Java
线程池中线程异常后:销毁还是复用?技术深度剖析
在并发编程中,线程池作为一种高效利用系统资源的工具,被广泛用于处理大量并发任务。然而,当线程池中的线程在执行任务时遇到异常,如何妥善处理这些异常线程成为了一个值得深入探讨的话题。本文将围绕“线程池中线程异常后:销毁还是复用?”这一主题,分享一些实践经验和理论思考。
120 3
|
3月前
|
Java 数据库连接 数据库
当线程中发生异常时的情况分析
【8月更文挑战第22天】
95 4
|
3月前
|
Java
线程池中线程抛了异常,该如何处理?
【8月更文挑战第27天】在Java多线程编程中,线程池(ThreadPool)是一种常用的并发处理工具,它能够有效地管理线程的生命周期,提高资源利用率,并简化并发编程的复杂性。然而,当线程池中的线程在执行任务时抛出异常,如果不妥善处理,这些异常可能会导致程序出现未预料的行为,甚至崩溃。因此,了解并掌握线程池异常处理机制至关重要。
371 0