• c++并发线程之创建线程、 join、detach、joinable、线程临时变量问题、ref使用


    join

    主线程等待子线程执行完

    
    #include <thread>
    
    using namespace std;
    
    /*
    a、每个进程都有一个主线程,这个主线程是唯一的,也就是一个进程只有一个主线程
    b、当你执行一个可执行程序,产生一个进程后,这个主线程就随着这个进程默默的启动了
    
    多线程(并发)
    //线程并不是越多越好,每一个线程,都需要一个独立的堆栈空间(1M),线程之间的切换要保存很多中间状态
    */
    
    void myprint()
    {
        cout << "开始执行线程" << endl;
    }
    
    int main()
    {
        //主线程从main函数开始执行
        //一般情况下,主线程执行完毕了,如果其他子线程还没有执行完毕,那么这些子线程也会被操作系统强行终止
    
        thread mythread(myprint);//1、创建了线程  2、线程执行起点myprint 3、myprint线程开始执行
    
        mythread.join();//希望主线程等待子线程,等待子线程执行完、阻塞主线程
    
        cout << "main func end" << endl;
    
        return 0;
    }
    
    • 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

    detach

    int main()
    {
    
        thread mythread(myprint);
    
        //这个子线程就相当于被c++运行时库时刻接管,当这个子线程执行完成后,由运行时库负责清理该线程相关的资源
        mythread.detach();//主线程与子线程失去关联,子线程在后台运行
        //主线程运行结束,那进程就结束了,子线程的打印就不输出了,在后台执行
        for (int i = 0; i < 10; i++) {
            cout << "main func end" << i << endl;
        }
    
        return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    当调用了detach后,不能再调用join了

    joinable

    int main()
    {
    
        thread mythread(myprint);
    
        //判断detach 或者 join能否被使用,如果返回true 就可以使用
        if (mythread.joinable()) {
            cout << "11111" << endl;
            mythread.join();
        }
    
        //当join 或者 detach被调用了,就返回false
        if (mythread.joinable()) {
            cout << "22222" << endl;
            mythread.detach();
        }
        
    
        for (int i = 0; i < 10; i++) {
            cout << "main func end" << i << endl;
        }
    
        return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24

    线程传参陷阱

    创建线程,不要在参数中传递指针、引用

    void myprint(const int &i, char * mybuf)
    {
        cout << i << endl;
        cout << mybuf << endl;
    }
    
    int main()
    {
        int var = 10;
        int& myvar = var;
        char buf[] = "this is buffer";
        //myvar这里是引用,本应该不会进行拷贝,但是确拷贝了,
        //所以就算主线程结束了,myvar回收了, myprint函数中的i也不会报错
        //线程对参数中的引用也会进行拷贝
        //但是指针不会拷贝,如果主线程的buf回收了,myprint中的 mybuf就会报错
        thread mythread(myprint, myvar,buf);
    
        mythread.detach();
    
        return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21

    在thread的参数中 创建临时对象传递给线程,一定能保证在主线程执行完之前,创建完成,thread中的参数,引用也会被拷贝一次

    class A
    {
    public:
        A(int i) :m_i(i) { cout << "传参构造函数" << this << endl; }
        A(const A& a) :m_i(a.m_i) {cout << "拷贝构造函数" << this << endl; }
        ~A() { cout << "析构函数" << this << endl; }
    int m_i;
    };
    
    void myprint(const int i, A a)
    {
        cout << a.m_i << endl;
    }
    
    int main()
    {
        int var = 10;
        int& myvar = var;
        char buf[] = "this is buffer";
        //添加A 临时对象,确保在main函数执行完之前,就能构造类对象
        //这样就执行了一次 创建临时对象,传参构造函数 和一次拷贝构造函数,
        thread mythread(myprint,var,A(var));
    
        mythread.detach();
    
        return 0;
    }
    
    • 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

    通过线程id看是在主线程中做的创建临时对象还是在 子线程创建的临时对象

    带入普通变量,隐式转换成A类对象

    class A
    {
    public:
        A(int i) :m_i(i) { cout << "传参构造函数" << this  << "thread id =" << this_thread::get_id() << endl; }
        A(const A& a) :m_i(a.m_i) {cout << "拷贝构造函数" << this << "thread id =" << this_thread::get_id() << endl; }
        ~A() { cout << "析构函数" << this << "thread id =" << this_thread::get_id() << endl; }
    int m_i;
    };
    
    void myprint(const int i, A a)
    {
        cout << a.m_i << endl;
    }
    
    int main()
    {
        int var = 10;
    
        //主线程id
        cout << "主线id是" << this_thread::get_id() << endl;
    
        thread mythread(myprint,var,var);
    
        mythread.join();
    
        return 0;
    }
    
    • 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

    说明带入变量做隐式转换,创建A类是在子线程中完成,如果主线程执行结束了,释放了var,就会出问题
    在这里插入图片描述

    带入A类对象,传参构造

    在这里插入图片描述

    说明创建临时变量,构造函数是在主线程中完成的
    在这里插入图片描述

    如何避免线程参数 进行拷贝构造呢?

    有时候带入的是引用,并且修改了引用的值,但是如果进行了拷贝构造,那么引用就没有意义了,修改的值 也仅仅只是修改 拷贝后对象的值, 如果 禁止拷贝构造?
    仅仅需要在thread中的 参数 加上 ref就可以

    void myprint(A &a)
    {
        a.m_i = 20;
        cout << "myprint" << a.m_i << endl;
        return;
    }
    
    int main()
    {
        int var = 10;
    
        //主线程id
        cout << "主线id是" << this_thread::get_id() << endl;
        A a(var);
        cout << "111 a.m_i = " << a.m_i << endl;
    	//加上ref后,就不会对a进行拷贝了,m_1也能被成功修改
        thread mythread(myprint,ref(a));
    
    
        mythread.join();
        cout << "222 a.m_i = " << a.m_i << endl;
        return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23

    在这里插入图片描述

  • 相关阅读:
    C# ComboBox 和 枚举类型(Enum)相互关联
    【Mybatis编程:根据若干个id批量删除相册(动态SQL)】
    redis实现分布式锁(包含代码以及分析利弊)
    初识AJAX基础(一)
    群狼调研(长沙口味测试)如何开展产品口味测试
    OOM和JVM最详细介绍
    利用概率学实现组合优化层,新研究构建Julia开源包InferOpt.jl
    【Java面试】什么!年薪50W就问这?我照着高手回答直接背能拿吗?请查收
    资深java面试题及答案整理(四)
    Docker:开启访问端口 || IDEA开发工具快速部署到服务器
  • 原文地址:https://blog.csdn.net/zzsxyl/article/details/125524698