在终止程序之前没有使用 join() 等待后台线程

前提分析:线程分为 joinable  状态和 detached 状态

添加 .join() 这句代码的时候,就表示主线程需要等待子线程运行结束回收掉子线程的资源后,再往下运行,否则就会产生一种情况:当子线程还没有运行完主线程先运行完了,那么就会结束这个进程,从而中断了子线程的运行。因此 join()函数的作用就是使主线程在此阻塞,等待子线程运行结束并回收其资源,再往下运行。

添加 .detach() 的作用是将主线程与子线程分离,主线程将不再等待子线程的运行,也就是说两个线程同时运行,当主线程结束的时候,进程结束。

在 detach  的时候,这个子线程将脱离主线程的控制,子线程独立分离出去并在后台运行。当主线程结束的时候,进程也就结束,所以子线程的输出不再显示出来,但是不会中断,会在后台继续运行,当子线程运行完以后,资源会被运行时库进行回收。

问题:如果我们在主程序终止之前忘记 join 或 detach,则会导致程序崩溃。

比如:

#include <iostream>
#include <thread> using namespace std; void LaunchRocket() { cout << "Launching Rocket" << endl; } int main() {
thread t1(LaunchRocket);
// t1.join(); // somehow we forgot to join this to main thread - will cause a
// crash.
return 0;
}

运行后,会报错。

解析:

这是因为在 main 函数结束时,线程 t1 超出范围后会自动析构,在析构过程中,会有一个检查来观察线程 t1 是否是 joinable 状态,如果是 joinable 状态,则调用 std::terminate

~thread() _NOEXCEPT
{ // clean up
if (joinable())
_XSTD terminate();
}

而我们创建的 t1 没有添加 .join(),还处于 joinable 状态,此时析构时会调用 terminate(),而 terminate() 会默认调用 abort(),从而报错

补充:

1. joinable() 代表该线程是可执行线程,用于检测线程是否有效。

它会返回一个布尔值来表示当前的线程是否是可执行线程(能被 join 或者 detach),因为相同的线程不能 join 两次,也不能 join  完再  detach,同理也不能 detach

通常以下几种情况会导致线程成为 not-joinable

1)由 thread 的缺省构造函数而造成的(thread() 没有参数)。

2)该 thread 被 move 过(包括 move 构造和 move 赋值)。

3)该线程被 join 或者 detach 过。

比如:

...

if (t1.joinable()) {
cout << "joinable() == true" << endl; // 没有添加 .join(),joinable() == true
} else {
cout << "joinable() == fasle" << endl; // 添加 .join(),joinable() == true
} ...

2. 摘自 https://en.cppreference.com/w/cpp/thread/thread/~thread

四种可以安全析构的情况是:

  1. 默认构造函数创建的 std::thread,在这种情况下,没有实际的线程被创建。
  2. 被移动过的线程,在这种情况下,移动的对象关联了线程而被移动的对象无关联线程。
  3. 调用了 join(),在这种情况下,join() 函数会堵塞直到被关联的线程执行结束。
  4. 调用了detach(),在这种情况下,被关联的线程会被解除关联。

参考:

1. C++ 线程问题,为什么会崩溃?

2. C++ 多线程中的join, detach, joinable

3. Top 20 C++ multithreading mistakes and how to avoid them

C++ 多线程的错误和如何避免(1)的更多相关文章

  1. c++多线程崩溃错误1

    主线程中的子线程没有jion,导致主线程马上结束,子线程对象被释放掉,而子线程还在后台继续执行导致崩溃 int main() OBJ = classA() OBJ.START()//在start函数中 ...

  2. Java SE之快速失败(Fast-Fail)与快速安全(Fast-Safe)的区别[集合与多线程/增强For](彻底详解)

    声明 特点:基于JDK源码进行分析. 研究费时费力,如需转载或摘要,请显著处注明出处,以尊重劳动研究成果:博客园 - https://www.cnblogs.com/johnnyzen/p/10547 ...

  3. Java多线程知识总结(一)

    一.创建线程的三种方式: 创建线程的方式有三种,一是创建Thread实例,二是实现Runnable接口,三是实现Callable接口,Runnable接口和Callable接口的区别是一个无返回值,一 ...

  4. Java回顾之多线程同步

    在这篇文章里,我们关注线程同步的话题.这是比多线程更复杂,稍不留意,我们就会“掉到坑里”,而且和单线程程序不同,多线程的错误是否每次都出现,也是不固定的,这给调试也带来了很大的挑战. 在这篇文章里,我 ...

  5. 2019年北航OO第二单元(多线程电梯任务)总结

    一.三次作业总结 1. 说在前面 对于这次的这三次电梯作业,我采用了和几乎所有人都不同的架构:将每个人当作一个线程.这样做有一定的好处:它使得整个问题的建模更加自然,并且在后期人员调度变得复杂时,可以 ...

  6. 转:最近5年133个Java面试问题列表

    最近5年133个Java面试问题列表 Java 面试随着时间的改变而改变.在过去的日子里,当你知道 String 和 StringBuilder 的区别就能让你直接进入第二轮面试,但是现在问题变得越来 ...

  7. [转载]hashmap hashtable 的区别

    Hashtable 和 HashMap 做为 Map 的基本特性 两者都实现了Map接口,基本特性相同 -          对同一个Key,只会有一个对应的value值存在 -          如 ...

  8. Java笔试题解答和部分面试题

    面试类  银行类的问题 问题一:在多线程环境中使用HashMap会有什么问题?在什么情况下使用get()方法会产生无限循环? HashMap本身没有什么问题,有没有问题取决于你是如何使用它的.比如,你 ...

  9. Java 集合系列 11 hashmap 和 hashtable 的区别

    java 集合系列目录: Java 集合系列 01 总体框架 Java 集合系列 02 Collection架构 Java 集合系列 03 ArrayList详细介绍(源码解析)和使用示例 Java ...

  10. 面试题_76_to_81_Java 最佳实践的面试问题

    包含 Java 中各个部分的最佳实践,如集合,字符串,IO,多线程,错误和异常处理,设计模式等等. 76)Java 中,编写多线程程序的时候你会遵循哪些最佳实践?(答案)这是我在写Java 并发程序的 ...

随机推荐

  1. [转帖]Rocksdb的优劣及应用场景分析

      研究Rocksdb已经有七个月的时间了,这期间阅读了它的大部分代码,对底层存储引擎进行了适配,同时也做了大量的测试.在正式研究之前由于对其在本地存储引擎这个江湖地位的膜拜,把它想象的很完美,深入摸 ...

  2. 【转帖】调教LLaMA类模型没那么难,LoRA将模型微调缩减到几小时

    https://www.thepaper.cn/newsDetail_forward_23250236 LoRA 微调方法,随着大模型的出现而走红. 最近几个月,ChatGPT 等一系列大语言模型(L ...

  3. 没有虚拟DOM版本的vue(Vue Vapor)

    前言 随着Svelte和SolidJS的流行,无虚拟DOM模式逐渐开始火了起来.vue也推出了无虚拟DOM模式的版本,就是我们今天要讲的Vue Vapor. 什么是Vue Vapor Vue Vapo ...

  4. 原生js中offsetTop, offsetLeft与offsetParent的详细讲解

    简单说下:offsetTop offsetTop: 为只读属性. 返回的是一个数字. 它返回当前元素相对于其 offsetParent 元素的顶部内边距的距离. 它等价于offsetTop==> ...

  5. echarts饼图中央自定义文字

    var option = { tooltip: { trigger: 'item' }, legend: { top: '5%', left: 'center' }, //中央自定义文字 title: ...

  6. vue中父传子props的使用

    第一种 传递一个数组 props:["cont"] 第二种 传递一个对象 props:{ uploadOption:{ type:Object, 参数类型必须是一个对象 requi ...

  7. 【解决了一个小问题】在某个linux基础镜像中安装python特定的版本

    作者:张富春(ahfuzhang),转载时请注明作者和引用链接,谢谢! cnblogs博客 zhihu Github 公众号:一本正经的瞎扯 在某个基础镜像中,安装了python3.6.但是一个测试需 ...

  8. 【学到了】golang的[]byte可以append string类型的数据

    上代码: func Test_use_string(t *testing.T){ arr := make([]byte,0, 100) arr = append(arr, "abcd&quo ...

  9. 【JS 逆向百例】网洛者反爬练习平台第四题:JSFuck 加密

    关注微信公众号:K哥爬虫,持续分享爬虫进阶.JS/安卓逆向等技术干货! 声明 本文章中所有内容仅供学习交流,抓包内容.敏感网址.数据接口均已做脱敏处理,严禁用于商业用途和非法用途,否则由此产生的一切后 ...

  10. 【JS 逆向百例】37网游登录接口参数逆向

    声明 本文章中所有内容仅供学习交流,严禁用于商业用途和非法用途,否则由此产生的一切后果均与作者无关,若有侵权,请联系我立即删除! 逆向目标 目标:37网游登录 主页:https://www.37.co ...