• C++11多线程第二篇:线程启动、结束、创建线程的多个方法:join、detach


    2、线程启动、结束、创建线程的多个方法:join、detach

    在这里插入图片描述

    2.1 范例演示线程运行的开始和结束

    • 程序运行起来,生成一个进程,该进程所属的主线程开始自动运行。
    std::cout<<"C++"<
    • 1
    • 实际上是这个主线程在执行,主线程从main()函数返回,则整个进程执行完毕。
    • 主线程从main()开始执行,那么我们自己创建的线程,也需要从一个函数开始运行(初始函数),一旦这个函数运行完毕,线程也结束运行。
    • 整个进程是否执行完毕的标志是 主线程是否执行完,如果主线程执行完毕了,就代表整个进程执行完毕了。
    • 此时,一般情况下,如果其他子线程还没有执行完毕,那么这些子线程也会被操作系统强行终止。
    • 所以,一般情况下,得到一个结论:如果大家想保持子线程(自己用代码创建的线程)运行状态的话,主线程不能停止运行。
    2.1.1 thread
    std::thread obj(MyPrint); //创建了线程,线程入口函数MyPrint(),然后MyPrint()函数开始执行。
    
    • 1
    • 创建线程主要包含以下几步:

      • 包含一个头文件thread。

      • 线程入口初始函数要写。

      • 入口初始函数是一个可以调用的对象。一组可以执行的语句称为可调用对象,C++中可调用对象可以是函数、函数指针、lambda表达式、bind绑定对象、还可以是重载了()的类对象( operator() )。

      • 必须要明白:有两个线程在跑,相当于整个程序中有两条线在同时走,即使一条被阻塞,另一条也能运行。

    thread是一个标准库的类。

    2.1.2 join()加入/汇合
    obj.join();  //阻止主线程执行,而是等待子线程执行完毕join()才算执行完毕,然后才执行主线程。
    
    • 1
    • 就是阻塞,阻塞主线程,让主线程等待子线程执行完毕,然后子线程和主线程汇合,在执行主线程代码。**

    • 一个良好的线程程序,应该是主线程等待子线程执行完毕之后,主线程才退出来。

    2.1.3 detach()分离
    obj.detach();  //主线程与子线程分离开来,就是主线程自己执行自己的主线程代码,子线程执行自己的子线程代码,主线程不必等待子线程运行结束。
    
    • 1
    • detach():主线程与子线程分离开来,就是主线程自己执行自己的主线程代码,子线程执行自己的子线程代码,主线程不必等待子线程运行结束。

    • 为什么引入detach():假如创建了许多子线程,让主线程逐个等待子线程结束,一般这种情况是不友好的,所以引入了detach()。一旦detach() 之后,与这个主线程关联的thread对象就会失去与主线程之间的关联,此时的子线程就会驻留在后台运行(主线程与子线程失去联系)。

    • 这个子线程就相当于被C++运行时库接管,当这个子线程执行完成后,就会由C++运行时库负责清理该线程相关的资源。(守护进程)

    • detach()使线程入口函数(子线程)失去对于自己的控制。

    2.1.4 joinable()
    • joinable()判断是否可以成功使用join()或者detach()的,返回true或者false。true表示可以使用join()或者detach(),false表示不可以使用join或者detach()。
    • 一旦调用了join()或者detach(),后续就不能在调用了。
    2.1.5 代码如下

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ETlFrVsN-1667578384042)(C:\Users\hp\AppData\Roaming\Typora\typora-user-images\image-20221012222049292.png)]

    #include
    #include
    
    void MyPrint(){
        std::cout<<"子线程开始--->"<"<"<"<"<"<"<"<"<"<"<
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43

    输出结果:

    主线程结束1
    主线程结束2
    主线程结束3
    主线程结束4
    子线程开始--->
    子线程结束1--->
    子线程结束2--->
    子线程结束3--->
    子线程结束4--->
    子线程结束5--->
    子线程结束6--->
    子线程结束7--->
    子线程结束8--->
    子线程结束9--->
    子线程结束10--->
    主线程结束5
    主线程结束6
    PS C:\Users\Administrator\Desktop\thread\build\thread\Debug> 
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18

    2.2 其他创建线程的手法

    入口初始函数是一个可以调用的对象。一组可以执行的语句称为可调用对象,C++中可调用对象可以是函数、函数指针、lambda表达式、bind绑定对象、还可以是重载了opetator()成员函数的类对象( operator() )。

    2.2.1 用类成员函数operator()、以及一个问题范例创建线程
    • 创建一个类,并写成员函数重载(),operator(),实例化化一个该类的对象,把该对象作为线程入口地址。
    #include
    #include
    
    class A{
    public:
        void operator()(){
            std::cout<<"子线程开始1"<

输出结果:

PS D:\C++11多线程代码编写\build\thread线程创建方法2\Debug> .\main.exe     
子线程开始1
子线程结束1
主线程结束
PS D:\C++11多线程代码编写\build\thread线程创建方法2\Debug>
  • 1
  • 2
  • 3
  • 4
  • 5
#include
#include

template
class A{
public:
    A(T& a)
    :a_(a)
    {}
    ~A(){}
    void operator()(){
        std::cout<<"子线程开始1"< a(b);
    std::thread obj(a);
    //obj.join();
    obj.detach();
    std::cout<<"主线程结束1"<

这时会发现输出结果会有不一样的结果:

主线程结束2
主线程结束3
主线程结束4
主线程结束5
主线程结束6
主线程结束7
主线程结束8
主线程结束9
主线程结束10子线程开始1
子线程结束1
a_1的值:
主线程结束1
主线程结束2
主线程结束3
主线程结束4
主线程结束5
主线程结束6
主线程结束7
主线程结束8
主线程结束9
主线程结束10
子线程开始1
主线程结束1
主线程结束2
主线程结束3
主线程结束4
主线程结束5
主线程结束6子线程开始1
子线程结束1
a_1的值:
主线程结束7
主线程结束810
a_2的值:10
a_3的值:10
a_4的值:10
a_5的值:10

主线程结束9
主线程结束10
PS D:\C++11多线程代码编写\build\thread线程创建方法2\Debug>
#include
#include

template
class A{
public:
    A(T a)
    :a_(a)
    {}
    ~A(){}
    void operator()(){
        std::cout<<"子线程开始1"< a(b);
    std::thread obj(a);
    //obj.join();
    obj.detach();
    std::cout<<"主线程结束1"<

输出结果:

PS D:\C++11多线程代码编写\build\thread线程创建方法2\Debug> .\main.exe     
主线程结束1
主线程结束2
主线程结束3
子线程开始1主线程结束4
主线程结束5
主线程结束6
主线程结束7
子线程结束1
a_1的值:
主线程结束810
a_2的值:10
a_3的值:10
a_4的值:10
a_5的值:10

主线程结束9
主线程结束10
PS D:\C++11多线程代码编写\build\thread线程创建方法2\Debug>
2.2.2 用lambda表达式
#include
#include

int main(){
    auto lambadThread=[](){
        std::cout<<"子线程开始1"<

输出结果:

PS D:\C++11多线程代码编写\build\thread线程创建方法3\Debug> .\main.exe
子线程开始1
子线程结束1
子线程结束2
子线程结束3
子线程结束4
主线程结束1
PS D:\C++11多线程代码编写\build\thread线程创建方法3\Debug>
  • 相关阅读:
    【wxWidgets 实现Window窗口系统】
    目标检测算法改进系列之Backbone替换为RepViT
    【LeetCode】36.有效的数独
    分布式二级缓存组件实战(Redis+Caffeine实现)
    快手发布Q2及半年度财报,哪些内容值得关注
    2173. 最多连胜的次数
    VERYCLOUD睿鸿股份确认参展2024年ChinaJoy BTOB商务洽谈馆,期待与你相聚
    调试bug心得
    STM32——DS18B20温度传感器
    大厂竞相入局 Rust,性能、安全是关键,揭晓 2021 年 Rust 开发者调查报告
  • 原文地址:https://blog.csdn.net/qq_44918090/article/details/127698647