前言

父母必须照管好孩子。线程也一样,父线程对子线程生命周期有很大的影响。
以下程序启动一个显示其ID的线程。

// threadForgetJoin.cpp#include <iostream>
#include <thread>int main()
{std::thread t([]{std::cout << std::this_thread::get_id() << std::endl;});
}

程序运行会导致意外结果:

这是啥原因呢?


加入和分离

创建的线程t的生命周期随着可调用它的单元的结束而结束了。
线程创建者有两种选择:

1、它等待,直到它的孩子完成(t.join())。
2、它把它的孩子t分离出去了(t.detach())。

在上面程序中,什么操作都没得,不管是t.join()还是t.detach(),所以这个线程析构时抛出std::terminate异常,因此,程序终止。
这就是原因,实际运行中意外终止。
这个问题的解决方案很简单。通过调用t.join(),程序可以正常运行。

// threadWithJoin.cpp#include <iostream>
#include <thread>int main()
{std::thread t([] {std::cout << std::this_thread::get_id() << std::endl; });t.join();
}

分离的挑战

当然,您可以在上面的程序中使用t.detach()而不是t.join()。
线程t不再可连接,它的析构函数不会调用std::terminate。看起来很糟糕,因为现在程序行为是未定义的,因为没有确保对象std::cout的生命周期。
该程序的执行有点奇怪:

我将在下一篇文章中详细阐述这个问题。


move线程

到现在为止,这还是容易理解的。但接下来要注意了。。。。
想要复制一个线程(复制语义)是不可能的,你只能move(移动语义)它。
如果一个线程被move,想以正确的方式处理它的生命周期要困难得多。

// threadMoved.cpp#include <iostream>
#include <thread>
#include <utility>int main()
{std::thread t([] {std::cout << std::this_thread::get_id(); });std::thread t2([] {std::cout << std::this_thread::get_id(); });t = std::move(t2);t.join();t2.join();
}

两个线程 t1和t2都只是干了个简单的活:打印它们的ID。
除此之外,Thread t2将被移动到t:t = std::move(t2);。
最后,主线程照顾它的孩子并等它们执行完毕。
可是等等。。。。。这是发生啥了?????

windows平台:

出了啥问题?有两个问题:

1、通过move(获取其所有权)线程t2,t获得一个新的可调用单元,并且将调用其析构函数。所以t的析构函数调用std::terminate,因为它仍然是可连接的。
2、但是线程t2没有相关的可调用单元。在没有可调用单元的线程上调用join会导致异常std::system_error。


修复这两个错误:

// threadMovedFixed.cpp#include <iostream>
#include <thread>
#include <utility>int main()
{std::thread t([]{std::cout << std::this_thread::get_id() << std::endl;});std::thread t2([]{std::cout << std::this_thread::get_id() << std::endl;});t.join();t = std::move(t2);t.join();std::cout << "\n";std::cout << std::boolalpha << "t2.joinable(): " << t2.joinable() << std::endl;system("pause");
}


scoped_thread

如果你嫌手动处理线程的生命周期太麻烦,可以在自己的包装类中封装std::thread。
这个类应该在析构函数中自动调用join。当然,你可以反过来调用detach。但是你知道,分离存在一些问题。
Anthony Williams创造了这样有价值的类,他称之为scoped_thread。
在构造函数中,它检查线程是否可连接,并最终在析构函数中join。
由于复制构造函数和复制赋值运算符被声明为delete,因此无法复制或分配scoped_thread的对象:

// scoped_thread.cpp#include <iostream>
#include <thread>
#include <utility>class scoped_thread
{std::thread t;
public:explicit scoped_thread(std::thread t_) : t(std::move(t_)) {if (!t.joinable()){throw std::logic_error("No thread");}}~scoped_thread() {t.join();}scoped_thread(scoped_thread&) = delete;scoped_thread& operator=(scoped_thread const &) = delete;
};int main()
{scoped_thread t(std::thread([] {std::cout << std::this_thread::get_id() << std::endl; }));return 0;
}

原文:

http://www.modernescpp.com/index.php/threads-lifetime

C++11线程的生命周期相关推荐

  1. java for(o t :object) 获取顺序号_java中线程的生命周期

    线程是java中绕不过去的一个话题, 今天本文将会详细讲解java中线程的生命周期,希望可以给大家一些启发. java中Thread的状态 java中Thread有6种状态,分别是: NEW – 新创 ...

  2. java中线程的生命周期

    文章目录 java中Thread的状态 NEW Runnable BLOCKED WAITING TIMED_WAITING TERMINATED java中线程的生命周期 线程是java中绕不过去的 ...

  3. Java中线程的生命周期-图解

    线程的生命周期:当一个线程被创建之后,进入新建状态,JVM则给他分配内存空间,并进行初始化操作.当线程对象调用了start()方法,该线程就处于就绪状态(可执行状态),JVM会为其创建方法调用栈.和程 ...

  4. java线程的生命周期及wait(),notify(),notifyAll()的详解分析

    1.java线程的生命周期     线程是一个动态执行的过程,它也有一个从产生到死亡的过程. (1)生命周期的五种状态 新建(new Thread)     当创建Thread类的一个实例(对象)时, ...

  5. Java 多线程— 线程的生命周期及方法

    2019独角兽企业重金招聘Python工程师标准>>> 这篇博客介绍线程的生命周期. 线程是一个动态执行的过程,它也有从创建到死亡的过程. 线程的几种状态 在 Thread 类中,有 ...

  6. java main生命周期_Java从入门到入土(62)线程的生命周期

    线程是程序内部的一个顺序控制流,他具有一个特定的生命周期.在一个线程的生命周期中,他总是处于某一种状态中.线程的状态表示了线程正在进行的活动以及在这段时间内线程能完成的任务. 线程的生命周期包括五个状 ...

  7. 线程的生命周期其实没有我们想象的那么简单!!

    来自:冰河技术 写在前面 在[高并发专题]中的<高并发之--线程与多线程>一文中,我们简单介绍了线程的生命周期和线程的几个重要状态,并以代码的形式实现了线程是如何进入各个状态的.今天,我们 ...

  8. java -- 线程的生命周期

    线程的生命周期 1.线程的生命周期线程是一个动态执行的过程,它也有一个从产生到死亡的过程. (1)生命周期的五种状态 新建(new Thread)当创建Thread类的一个实例(对象)时,此线程进入新 ...

  9. Java并发编程:线程的生命周期是个怎样的过程?

    前言 在日常开发过程中,如果我们需要执行一些比较耗时的程序的话,一般来说都是开启一个新线程,把耗时的代码放在线程里,然后开启线程执行.但线程是会耗费系统资源的,如果有多个线程同时运行,互相之间抢占系统 ...

  10. Java的知识点28——线程的生命周期

    线程的生命周期 一个线程对象在它的生命周期内,需要经历5个状态. 1.  新生状态(New): 用new关键字建立一个线程对象后,该线程对象就处于新生状态.处于新生状态的线程有自己的内存空间,通过调用 ...

最新文章

  1. maven项目中 把依赖的jar包一起打包
  2. winform利用CefSharp调用google浏览器内核ChromiumWebBrowser,与JS交互
  3. AI Debate2 | 李飞飞提「AI北极星」,卡尼曼指「系统 1」理解有误
  4. Oracle undo 表空间管理
  5. 怎么查询linux中的组密码,linux查看用户组中的用户
  6. 华为笔记本Win11更新时由于驱动问题引起蓝牙鼠标经常断开问题解决方法
  7. node那点事(二) -- Writable streams(可写流)、自定义流
  8. mysql10.3修改默认存储路径
  9. 利用InfoPath生成XML资源文件
  10. CCF NOI1038 玩扑克
  11. ET城市大脑出海:阿里云发展世界级创新技术业务
  12. Chrome:下载Chrome网上应用店扩展程序crx
  13. 冻库正常低压力是多少_零下40度低温低压压力多少算正常?
  14. 关于nmap和traceroute在虚拟机里使用的问题
  15. Java 中的代理(proxy)
  16. iphone邮箱收件服务器设置,iphone中使用国内邮箱设置方法
  17. firefly-rk3288开发板Linux驱动——LED驱动
  18. (精华)2020年10月7日 高并发高可用 Redis实现异步架构
  19. opc服务器不稳定的原因,OPC Client和OPC Server容易出现通讯故障,原因是什么?
  20. 定义商品类Goods 并进行封装

热门文章

  1. QQ快速登录协议分析
  2. 批处理 文件注释_批处理文件注释
  3. ant design pro 实现审核图片盖章功能
  4. 虚拟仿真实验平台服务器需求,虚拟仿真实验中心平台建设方案.pptx
  5. 继云计算巨头失火后,微软决定送数据中心去“泡澡”!
  6. 传染病模型中作图与计算(matlab,数学模型)
  7. android 关联dicom文件,基于Android的DICOM浏览器的开发
  8. c++语言判断是否质数,怎样用C++程序判断一个数是否为素数
  9. android平板生产力工具,重塑应用生态,让安卓平板成为生产力工具:华为MatePad Pro体验...
  10. 本周小折腾记录: ipad和电脑完成同屏功能