C++并发与多线程(一)线程的创建与退出、join、detach理解

文章目录

    • 线程的创建
      • 全局函数创建线程
      • 仿函数创建线程(类、重载()运算符)
      • lamda表达式
    • join()理解
    • detach()理解
    • joinable()

线程的创建

程序运行起来,生成一个进程,该进程所属的主线程开始自动运行;当主线程从main()函数返回,则整个进程执行完毕。

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

整个进程是否执行完毕的标志是:主线程是否执行完,如果主线程执行完毕了,就代表整个进程执行完毕了,此时如果其他子线程还没有执行完,也会被强行终止。

线程类参数是一个可调用对象。

一组可执行的语句称为可调用对象,c++中的可调用对象可以是函数函数指针lambda表达式bind创建的对象重载()运算符的类对象。

全局函数创建线程

线程创建的步骤:

  1. 包含头文件thread
  2. 写初始函数
  3. 在main中创建thread

创建线程代码如下:

#include 
using namespace std;
//包含头文件
#include  
//初始函数
void myPrint() {
	cout << "我的线程开始执行!" << endl;
	//......子进程过程体
	cout << "我的线程执行完毕!" << endl;
}
//main中创建thread
int main(){
	cout << "主线程开始执行!" << endl; 
	thread mythread(myPrint); //创建线程:线程执行入口myPrint(myPrint开始执行)
	mythread.join(); //join()阻塞主线程,让主线程等待子线程执行完毕,让子线程和主线程汇合
	cout << "主线程执行完毕!" << endl;
}

运行结果如下:
C++并发与多线程(一)线程的创建与退出、join、detach理解_第1张图片

仿函数创建线程(类、重载()运算符)

//仿函数创建线程
class MyPrint {
public:
	void operator()() {
		cout << "我的线程开始执行!" << endl;
		//......
		cout << "我的线程执行完毕!" << endl;
	}
};

//main中创建thread
int main(){
	cout << "主线程开始执行!" << endl; 

	MyPrint myPrint;
	thread mythread(myPrint); //创建线程:线程执行入口myPrint(myPrint开始执行)
	mythread.join(); //join()阻塞主线程,让主线程等待子线程执行完毕,让子线程和主线程汇合
	cout << "主线程执行完毕!" << endl;
}

这里想提醒注意一些点:

如果使用了detach(),主线程不能再控制子线程,如果使用仿函数可能会有意料之外的情况。

  1. 如果MyPrint类中有成员是使用的引用,这会有问题出现,因为主线程在运行结束后,就会释放空间,此时创建的类中的引用会被释放掉,因此子线程再访问就会出问题。编码时要注意,避免detach()和引用同时使用。
  2. 注意thread mythread(myPrint);创建线程的时候,传入类对象的时候,使用的是拷贝构造,因此不会出现主线程把对象myPrint释放掉,子线程会受影响的问题。但是这里使用的是浅拷贝,注意可能发生的隐藏bug。

lamda表达式

//main中创建thread
int main(){
	cout << "主线程开始执行!" << endl; 
	auto lambdaThread = [] {
		cout << "我的线程开始执行了" << endl;
		//-------------
		//-------------
		cout << "我的线程开始执行了" << endl;
	};

	thread mythread(lambdaThread);//创建线程:线程执行入口lambdaThread(lambdaThread开始执行)
	mythread.join();//join()阻塞主线程,让主线程等待子线程执行完毕,让子线程和主线程汇合
	cout << "主线程执行完毕!" << endl;
}

join()理解

  1. join()的作用是阻塞主线程,让主线程等待子线程执行完毕,让子线程和主线程汇合。
  2. 如果设置断点单步调试可以发现,thread创建子线程执行后,myPrint会先执行,然后会由主线程执行,执行到mythread.join()后会停下来等待子进程执行完毕。
  3. 总之,join()的意思就是主线程等待子线程,myPrint()执行完毕等价于mythread.join()执行完毕。

我们用代码来验证,如果我们在join()前加一个打印输出:

int main(){
	cout << "主线程开始执行!" << endl; 
	thread mythread(myPrint); //创建线程:线程执行入口myPrint(myPrint开始执行)

	cout << "main" << endl;  //打印main验证join功能

	mythread.join(); //join()阻塞主线程,让主线程等待子线程执行完毕,让子线程和主线程汇合
	cout << "主线程执行完毕!" << endl;
}

C++并发与多线程(一)线程的创建与退出、join、detach理解_第2张图片

多次尝试,可以看到,有时候main打印在myPrint结束后,有时候打印在myPrint执行过程中,这说明,主进程和子进程在不断切换,join()的作用是让主进程停下来等待子进程执行结束,不然主进程执行完毕退出,子进程也会被杀死(如果此时子进程在写某些文件,中途退出会造成非常严重的后果)。

注意这里:因为涉及多线程,输出的结果难以复现,所以要多次尝试,才可能得到main的输出在myPrint输出之间的情况/

PS:尽管后面的detach支持主进程不再控制子进程,但是还是推荐这种写法,便于管控。

detach()理解

传统多线程,主线程要等待子线程执行结束才可以退出,但是detach的出现改变了这种情况。

  1. detach():分离,主线程不再与子线程汇合,不再等待子线程 。
  2. detach()后,子线程和主线程失去关联,驻留在后台,由C++运行时库接管。
  3. 只要使用了detach(),就不能再使用join(),不然会报错。

代码验证如下:

//初始函数
void myPrint() {

	string nameSeed = "123456789";
	for (int i = 0; i < 9; i++) {
		string name = "子线程正在执行";
		name += nameSeed[i];
		cout << name << endl;
	}
}
//main中创建thread
int main(){
	cout << "主线程开始执行!" << endl; 
	thread mythread(myPrint); //创建线程:线程执行入口myPrint(myPrint开始执行)
	mythread.detach(); //子线程与主线程分离
	nstring nameSeed = "123456789";
yu	for (int i = 0; i < 9; i++) {
		string name = "main线程正在执行";
		name += nameSeed[i];
		cout << name << endl;  //打印10次拖延主线程运行时间,便于查看打印效果
	}
	cout << "主线程执行完毕!" << endl;
}

运行结果如下:
C++并发与多线程(一)线程的创建与退出、join、detach理解_第3张图片
这是一种非常标准的结果,主进程与子进程,你一句我一句打印,似乎没什么问题,但如果我们讲父进程中的循环次数改少一点,改为6,输出如下:
C++并发与多线程(一)线程的创建与退出、join、detach理解_第4张图片
可以看到,子进程根本打印不完9次,仅仅执行到6,就因为主进程退出了,无法显示在屏幕上了,但是事实上,他还是会在后台运行,继续打印剩下的3个。

PS:一旦使用了detach(),子进程就已经脱离了我们的控制,也不能再次join()了,我们已经不能控制我们创建的线程的生命周期,而是由系统管理释放。

joinable()

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

  1. 如果返回true,证明可以调用join()或者detach()
  2. 如果返回false,证明调用过join()或者detach(),join()和detach()都不能再调用了
if (mythread.joinable())
	{
		cout << "可以调用join()或者detach()" << endl;
	}
	else
	{
		cout << "不能调用join()或者detach()" << endl;
	}

无论在join()之后detach();或者detach()之后join(),都是不被允许的。

joinable()就是用来判断是不是可以调用join()或者detach()。

你可能感兴趣的:(C++,c++,多线程)