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

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

# include
# include//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;
}

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

# include
# include

using namespace std;

class A
{
public:
	//类型转换构造函数
	A(int i):x(i)
	{
		cout <<"***********构造函数***********" <<"this:"<

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

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

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

你可能感兴趣的:(c/c++)