C++11多线程注意事项以及detach中的坑

多线程编程是必须要掌握的,以前多线程基本是靠系统API或者第三方库完成的,比如windows的API函数CreateThread,linux创建线程函数pthread_create,但是这样编写的代码不可移植,不能跨平台,比如windows的多线程程序拿到linux下是跑不起来的,相反也一样,很不方便,然而C++11语言本身支持多线程,和平台无关,下面就来简单认识多线程

# include<iostream>
# include<thread>//C++11本身支持的多线程,需包含该头文件

using namespace std;

void thread1();//子线程1(可调用对象作为线程的入口)

int main()
{
	//一个.exe可执行程序即为一个进程,一个进程可以有一个或多个线程,其中有一个主线程
	//这里C++编写完成生成.exe可执行程序,main中的代码执行的就是主线程要做的事,我们自己进行多线程编程
	//可以定义一个函数,在函数中编写要做什么事的代码,多线程是并发执行的
	thread myThread(thread1);//线程一旦被创建就开始执行了,与main并发执行的,在遇到join之前,
	//可能一会执行main函数,一会执行thread1函数,是不确定的,每次运行结果都可能不一样
	cout << "主线程开始!" << endl;
	cout << "我是main函数1" << endl;
	cout << "我是main函数2" << endl;
	cout << "我是main函数3" << endl;
	cout << "我是main函数4" << endl;
	if(myThread.joinable())//可以加入时才能加入主线程
		myThread.join();//join表示加入,汇合,即加入进程
	//设想一下,一个进程可以包含多个线程,但只有一个主线程,主线
	//程执行完毕,进程也就结束了,所以传统多线程编程中,是让主线程等待其它线程执行完毕,然后主线程才能结束
	//不然,主线程都结束了,该进程也就结束了,但是那些未结束的其他线程,怎么办?比如其他线程也在往进程输入
	//输出,而进程都结束了,肯定会出错,抛出异常,主线程都执行完毕了,而子线程和主线程都属于进程,进程都结束了
	//但是子线程还在执行,比如子线程要访问进程中的资源(屏幕,文件等),这样的程序肯定是不合格的!
	//join简单来说,就是告诉主线程,我们是相关的,有关联的,属于同一进程的,你,必须等我执行完毕,才能继续执行
	//因为属于同一进程
	//一旦join,那么main函数一旦执行完join,就会等待thread1执行完毕再继续执行
	

	//
	cout << "我是main函数5" << endl;
	cout << "我是main函数6" << endl;
	cout << "我是main函数7" << endl;
	cout << "我是main函数8" << endl;
	cout << "主线程结束!" << endl;


	return 0;
}

void thread1()
{
	cout << "thread 1 begin!" << endl;
	cout << "I‘m thread 1 01" << endl;
	cout << "I‘m thread 1 02" << endl;
	cout << "I‘m thread 1 03" << endl;
	cout << "I‘m thread 1 04" << endl;
	cout << "I‘m thread 1 05" << endl;
	cout << "I‘m thread 1 06" << endl;
	cout << "thread 1 end!" << endl;
}

在这里插入图片描述结果很容易看到,执行顺序是不确定的,因为主线程main和子线程thread1并发执行,就像两条平行线往下一起执行,系统一会调度main线程,一会调度thread线程,但是,一旦调用join,主线程就会等待子线程运行结束,然后主线程在运行,直到结束,传统编程就是如此,试想如果子线程访问主线程中的资源,如果主线程结束了,其中资源释放,但是子线程还在运行,要访问资源,这样的话肯定会出问题的,现在介绍detach,即分离的意思,一旦detach,就讲子线程交给系统托管,与进程,主线程无关了,这种情况下,就很可能主线程结束,子线程还在运行,所以detach就引发出问题,编程的难度也加大了
在这里插入图片描述试着不调用join和deteach
在这里插入图片描述 系统抛出异常,很简单,一个.exe可执行程序就是一个进程,一个进程包含多个线程,其中有一个主线程,主线程结束了,进程也就结束了,但是子线程没有detach,和主线程还是有关联的,而主线程结束了,子线程还未结束,显然是不允许的,抛出异常
接下来主要讲detach
由于函数参数涉及到值传递和引用传递,所以问题就来了,看下面代码

# include<iostream>
# include<thread>

using namespace std;

class A
{
public:
	//类型转换构造函数
	A(int i):x(i)
	{
		cout <<"***********构造函数***********" <<"this:"<<this<<" "<< endl; 
	}
	A(const A&a)
	{
		cout << "*********拷贝构造函数*********" << "this:" << this << " " << endl;
	}
	~A() 
	{ 
		cout << "**********析构函数**********" << "this:" << this << " " << endl; 
	}
private:
	int x;
};

void fun(const int &i,const A &aa)//虽然这里参数是A的引用,但是不起作用,运行时还是会
{
	cout << "i="<<i << endl;
	//cout << "p=" << p << endl;
	cout << "thread begin" << endl;
	cout << "thread01" << endl;
	cout << "thread02" << endl;
	cout << "thread03" << endl;
	cout << "thread04" << endl;
	cout << "thread end" << endl;
}
int main()
{
	int a = 4;
	int &i = a;
	A aObj(9);
	//thread t(fun, i,b);//隐式转换,将int隐式转换为A,什么时候转换呢?执行A的构造函数才转换,先是传入参数到
	//thread的构造函数(这时主线程main和子线程fun就开始并发执行了),然后再是将后面的参数传到fun,这是fun的第二个参数是A类型的,而b是int的,这时会发生隐式类型转换
	//而到了这个时候,由于detach,很可能主线程已经执行完毕了,子线程还没构造完,所以fun里a引用b,b在主线程结束后释放了,没有了,所以肯定会有问题
	thread t(fun, i, aObj);//但是如果A(b)这样构造一个临时对象,在执行thread的构造函数时就已经构造一个临时变量
	//来接收b,就算detach后主线程结束,也不会有问题
	t.join();

	
	return 0;
}

将类作为一个参数传递给thread类的构造函数,可以看到传递时参数的复制情况,只要在拷贝构造函数中输出相应信息即可,先看看结果
在这里插入图片描述俩个构造函数说明构造了两个A的对象,一个是main函数里的aobj对象这个很好理解,那么另一个是哪里的呢?子线程入口fun里面参数是引用啊,按理来说不会产生临时变量拷贝A啊,到底第二个A对象是怎么来的呢,慢慢调试看
在这里插入图片描述

在这里插入图片描述在这里插入图片描述接下来执行到此处
在这里插入图片描述果然,继续调试,就转到拷贝构造函数来了,其实fun函数里传递引用,引用的是thread类构造时产生的临时变量,并不是aObj,所以主线程结束的话,子线程就算没结束也没关系
再把join改为detach,看看主线程结束子线程没结束是什么结果
在这里插入图片描述嗯,看起来没问题,把mian中代码改一下,在t的第3个参数传入一个整数,由于A定义了只接受一个参数的构造函数,也就是类型转换构造函数,
看看会输出什么
在这里插入图片描述W ! H ! A ! T !!!
为什么呢?仔细思考,之前传入aObj,在执行thread对象t的构造函数时,我们就拷贝aObj的临时对象,注意注意,是在构造thread对象的时候就拷贝,这时thread t(fun,i,aObj)语句还没执行完,所以会打印出很多信息,但是此时换成了int型变量b,构造thread对象时不会将b转换成A对象,而是在构造子线程,将参数i,b传递给fun时,才会对b进行转换,而由于主线程中代码较少,很快就结束了,甚至子线程还未构造main线程就结束了,所以才会有上面这种情况,

在这里插入图片描述
现在应该清楚了,看教学视频看到有的说是这里子线程还未构造,主线程就结束了,那么子线程在后台构造,但是b是main里的资源,main线程结束了,所以b被释放了,然后子线程再去访问b,所以容易出问题了,,我觉得这种说法不对,准确来说应该是构造thread时,参数会被复制到线程堆栈中供线程执行时存取,访问的是临时变量,而不是b,所以不会出问题,不存在访问已经释放的资源这个说法

  • 14
    点赞
  • 54
    收藏
    觉得还不错? 一键收藏
  • 8
    评论
评论 8
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值