c++11并发与多线程【第二节】


一、线程运行的开始结束范例

程序运行起来后,生成一个进程,该进程所属的主线程自动开始运行;

//一个单独的main函数,运行实际上是主线程在执行,主线程从main返回,则整个进程执行完毕
int main(){
    cout<<"I love china"<<endl;
    return 0;
}

主线程从main()函数开始执行,那么我们自己创建的线程也需要从一个函数开始运行(即初始函数),一旦这个函数运行完毕,就代表着我们的线程运行结束。

整个进程是否执行完毕的标志是:主线程是否执行完。一般情况下,如果主线程执行完毕,但是其他子线程(自己用代码创建的线程)还没执行完毕,那么这些子线程就会被操作系统强行终止。

因此,要保证子线程顺利执行,主线程不能先于子线程执行完毕。

创建一个线程:
a.需要包含头文件 thread.h;
b.编写初始函数;
c.main函数中编写线程代码

//自己创建的线程需要从一个函数开始运行
void MyPrint(){
    cout<<"我的线程开始"<<endl;
    
    cout<<"我的线程结束了"<<endl;
}

int main(){
    //此时这个代码中有两个线程在同时在执行,同时执行两个任务
    thread mytobj(MyPrint);
    mytobj.join();
    cout<<"I love china"<<endl;
    return 0;
}

1.1 thread类:位于C++11之后标准库中的类

  • thread mytobj(MyPrint);//创建了线程,并且以Myprint()为初始函数,然后MyPrint() 线程开始执行;

1.2 join():加入/汇合,实质上就是让主线程阻塞,使得主线程等待子线程执行完毕,然后主线程和子线程汇合

  • mytobj.join(); //主线程在这里阻塞,直到子线程MyPrint 执行完毕
#include<iostream>
#include<vector>
#include<thread>
using namespace std;
//一个单独的main函数,运行实际上是主线程在执行,主线程从main返回,则整个进程执行完毕

//自己创建的线程需要从一个函数开始运行
void MyPrint(){
    cout<<"我的线程开始1"<<endl;
    cout<<"我的线程开始2"<<endl;
    cout<<"我的线程开始3"<<endl;
    cout<<"我的线程开始4"<<endl;
    cout<<"我的线程开始5"<<endl;
    
    cout<<"我的线程结束了"<<endl;
}

int main(){
    //此时这个代码中有两个线程在同时在执行,同时执行两个任务
    thread mytobj(MyPrint);
    mytobj.join();
    //mytobj.detach();
    cout<<"I love china1"<<endl;
    cout<<"I love china2"<<endl;
    cout<<"I love china3"<<endl;
    cout<<"I love china4"<<endl;
    return 0;
}

在这里插入图片描述

1.3 detach() :通常主线程需要在子线程完成后再退出,detach()是个例外

detach:分离,主线程不和子线程汇合了,二者不必再互相等待运行结束。
一旦detach之后,与主线程关联的的thread线程对象,就会失去与主线程的关联,各走各的路,此时这个子线程就会驻留在后台运行(主线程与子线程失去联系),这个子线程相当于被C++运行时库接管,此时该子线程叫做守护线程;当这个子线程执行完成后,有运行时库负责清理该线程相关的资源。

int main(){
    //此时这个代码中有两个线程在同时在执行,同时执行两个任务
    thread mytobj(MyPrint);
    //mytobj.join();
    mytobj.detach();
    cout<<"I love china1"<<endl;
    cout<<"I love china2"<<endl;
    cout<<"I love china3"<<endl;
    cout<<"I love china4"<<endl;
    return 0;
}

在这里插入图片描述
由于主线程先结束了,因此子线程无法继续在控制台上打印信息。

注:一旦调用了detach(),不能再用join().

1.4 joinable():判断是否可以成功使用join()或者detach()


int main(){
    //此时这个代码中有两个线程在同时在执行,同时执行两个任务
    thread mytobj(MyPrint);
    if(mytobj.joinable()){
        cout<<"it can join/detach"<<endl;
    }
    else
        cout<<"it can not join/detach"<<endl;
    //mytobj.join();
    mytobj.detach();
    if(mytobj.joinable()){
        cout<<"2:it can join/detach"<<endl;
    }
    else
        cout<<"2:it can not join/detach"<<endl;

    cout<<"I love china1"<<endl;
    cout<<"I love china2"<<endl;
    cout<<"I love china3"<<endl;
    cout<<"I love china4"<<endl;
    return 0;
}

二、其他创建线程的方法

thread mytobj(MyPrint);//使用一个函数作为参数,该参数称为可调用对象

可调用对象除了函数之外,还有其他的。

2.1 类对象作为可调用对象

#include<iostream>
#include<vector>
#include<thread>
using namespace std;

class TA
{
    public:
    //重载()
        void operator()(){//不能带参数
            cout<<"我的Class线程开始1"<<endl;
            cout<<"我的Class线程结束1"<<endl;
        }
};

int main(){
    TA ta;
    thread mytobj3(ta);
    mytobj3.join();
    //mytobj3.detach();

    cout<<"I love china1"<<endl;
    cout<<"I love china2"<<endl;
    cout<<"I love china3"<<endl;
    cout<<"I love china4"<<endl;
    return 0;
}

输出:

我的Class线程开始1
我的Class线程结束1
I love china1
I love china2
I love china3
I love china4

注意:使用类对象作为可调用对象时,使用detach()可能会出现的问题。

class TA
{
    public:
        int &m_i;//成员变量是个引用
        //构造函数的参数也是个引用
        TA(int &i):m_i(i){}
        //重载()
        void operator()(){//不能带参数
            cout<<"m_i 1的值为"<<m_i<<endl;
            cout<<"m_i 2的值为"<<m_i<<endl;
            cout<<"m_i 3的值为"<<m_i<<endl;
            cout<<"m_i 4的值为"<<m_i<<endl;
            cout<<"m_i 5的值为"<<m_i<<endl;
        }
};

int main(){
    int my_i=6;
    TA ta(my_i);
    thread mytobj3(ta);
    //mytobj3.join();
    mytobj3.detach();

    cout<<"I love china1"<<endl;
    cout<<"I love china2"<<endl;
    cout<<"I love china3"<<endl;
    cout<<"I love china4"<<endl;
    return 0;
}

如上面这段代码,使用detach之后,倘若主线程在子线程之前执行完毕,main函数中的局部变量my_i就会被系统回收内存,此时该内存内的值是不可预知的。但是子线程还没执行完毕,还在打印这一内存内的值,就会产生不可预料的后果。

m_i 1的值为I love china1
I love china2
I love china3
I love china4
6
m_i 2的值为0
m_i 3的值为0
m_i 4的值为0
m_i 5的值为0

问题:一旦调用detach()之后,主线程若先于子线程结束,main函数中的TA对象也是局部的,也会被系统回收内存,为什么不会出错呢?

答:这个对象实际上是被复制到线程中去的,所以执行完主线程之后,对象ta会被摧毁,但是所复制的TA对象依旧存在。因此,只要这个TA类对象中没有引用或指针,就不会产生问题。

2.2 使用lambda表达式作为可调用对象创建线程

int main(){
    //使用lambda表达式创建线程
    auto my_lambda_thread=[]{
        cout<<"我的lambda线程开始执行了"<<endl;
        cout<<"我的lambda线程结束执行了"<<endl;
    };
    thread mytobj4(my_lambda_thread);
    mytobj4.join();
    //mytobj4.detach();
    cout<<"I love china1"<<endl;
    return 0;
}

文章内容来源《C++并发与多线程视频课程》

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值