当前位置: 首页 > news >正文

线程与异常

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

相关文章:

  • 【转载】一个优秀求职者应主动问的一些问题
  • 关于Flux,Vuex,Redux的思考
  • 文字跑马灯
  • 我掌握的linux防火墙知识
  • HttpResponseMessage 调用.net web api
  • CentOS下Samba文件服务器的安装与配置
  • 跟着百度学PHP[4]OOP面对对象编程-12-抽象类
  • 工作中用到的自定义控件
  • MySQL传统主从复制(第一弹)
  • MySQL数据库中日期中包涵零值的问题
  • 添加第三方源需要执行更新的时候报错,倒入公钥
  • JavaScript总结3
  • 一个绚丽的downloading动效分析与实现!
  • 判断2的幂次方(多种算法)
  • VMware中装Win2012并配置Hyper-v
  • 【翻译】Mashape是如何管理15000个API和微服务的(三)
  • 2017年终总结、随想
  • Android 初级面试者拾遗(前台界面篇)之 Activity 和 Fragment
  • Angular4 模板式表单用法以及验证
  • CentOS 7 修改主机名
  • EOS是什么
  • Iterator 和 for...of 循环
  • JavaScript服务器推送技术之 WebSocket
  • mac修复ab及siege安装
  • Node 版本管理
  • nodejs:开发并发布一个nodejs包
  • SwizzleMethod 黑魔法
  • VUE es6技巧写法(持续更新中~~~)
  • vue-router的history模式发布配置
  • 阿里云ubuntu14.04 Nginx反向代理Nodejs
  • 闭包--闭包作用之保存(一)
  • 实战:基于Spring Boot快速开发RESTful风格API接口
  • 网页视频流m3u8/ts视频下载
  • 学习JavaScript数据结构与算法 — 树
  • 因为阿里,他们成了“杭漂”
  • 自制字幕遮挡器
  • CMake 入门1/5:基于阿里云 ECS搭建体验环境
  • #【QT 5 调试软件后,发布相关:软件生成exe文件 + 文件打包】
  • #控制台大学课堂点名问题_课堂随机点名
  • #每日一题合集#牛客JZ23-JZ33
  • #周末课堂# 【Linux + JVM + Mysql高级性能优化班】(火热报名中~~~)
  • (2/2) 为了理解 UWP 的启动流程,我从零开始创建了一个 UWP 程序
  • (aiohttp-asyncio-FFmpeg-Docker-SRS)实现异步摄像头转码服务器
  • (C#)if (this == null)?你在逗我,this 怎么可能为 null!用 IL 编译和反编译看穿一切
  • (NO.00004)iOS实现打砖块游戏(十二):伸缩自如,我是如意金箍棒(上)!
  • (安卓)跳转应用市场APP详情页的方式
  • (转)JAVA中的堆栈
  • (转)Unity3DUnity3D在android下调试
  • (转)关于多人操作数据的处理策略
  • .describe() python_Python-Win32com-Excel
  • .gitignore文件_Git:.gitignore
  • .NET Core跨平台微服务学习资源
  • .NET Core中Emit的使用
  • .NET 简介:跨平台、开源、高性能的开发平台
  • .NET 实现 NTFS 文件系统的硬链接 mklink /J(Junction)