C++线程运行的开始和结束(代码演示)

一 主线程:

int main()
{
	//程序运行起来,生成一个进程,该进程所属的主线程开始自动运行
	cout << " I love China" << endl;//实际上这是主线程在执行,主线程从main()函数返回,则整个进程执行完毕。


	return 0;
}
  1. 主线程从main()函数开始执行,那么我们自己创建的线程,也需要从一个函数开始运行(初始函数)一旦这个函数运行完毕,就代表我们这个线程运行结束。
  2. 整个进程是否执行完毕的标志是:主线程是否执行完,如果主线程执行完毕了,就代表整个进程执行完毕了,此时如果其他子线程还没有执行完,也会被强行终止。
  3. 所以,一般情况下:如果想保持子线程(自己用代码创建的线程)的运行状态的话,就要让主线程一直保持运行,不要让主线程运行完毕。(不过也有例外)

二 、子线程创建

  • 包含头文件thread
#include
  • 我们自己创建的线程,也需要从一个函数开始运行(初始函数)
#include
#include
#include
#include
#include
using namespace std;

//自己创建的线程,而需要从一个函数开始运行(初始函数)
void myprint()
{
	cout << " 我的线程开始了" << endl;
	cout << " 我的线程执行完毕" << endl;
}
int main()
{
	//程序运行起来,生成一个进程,该进程所属的主线程开始自动运行
	//cout << " I love China" << endl;//实际上这是主线程在执行,主线程从main()函数返回,则整个进程执行完毕。

	return 0;
}
  • main()中开始写代码
void myprint()
{
	cout << " 我的线程开始了" << endl;
	cout << " 我的线程执行完毕" << endl;
}
int main()
{

	thread myobj(myprint);//thread是一个类,创建一个类对象myobj
	myobj.join();

	cout << " I love China" << endl;

	return 0;
}

此时的结果:C++线程运行的开始和结束(代码演示)_第1张图片
从结果来看,似乎是先运行的子线程,再运行的主线程
必须明确的是:有两个线程在跑,相当于整个程序中有两条线在同时走,即使一条被阻塞,另一条也能运行。

对于thread myobj(myprint);,这条语句包括以下要点:

  • thread是一个,创建一个类对象myobj,而myprint为可调用对象(作为实参去构造myobj对象)
  • 创建了线程,线程执行起点(入口)是myPrint;
  • myprint线程开始执行(即运行到这条语句时,子线程就开始执行了)

对于myobj.join();,这条语句包括以下要点:

  • join():汇合,即阻塞,阻塞主线程,让主线程等待子线程执行完毕,然后子线程和子线程汇合,然后主线程再往下走。
  • 即主线程阻塞到这里等待myprint()执行完,当子线程执行完毕,这个jion就执行完毕,主线程就继续往下走。
  • 一句话:阻塞主线程并等待子线程执行完

如果不加join这句话,程序就会报错,打印结果是混乱的。

三、detach()

一般情况下:如果想保持子线程(自己用代码创建的线程)的运行状态的话,就要让主线程一直保持运行,不要让主线程运行完毕。
当我们创建了很多子线程,让主线程等待子线程们逐个结束,这种编程方法并不好,
所以出现了detach,进行线程分离,即主线程不和子线程汇合,各走各的;

  • 一旦detach之后,与主线程关联的thread对象(myobj)就会失去与主线程的关联,此时这个子线程就会驻留在后台运行。这个子线程就相当于被C++运行时库接管,子线程执行完毕后,由运行时库负责清理该线程相关资源(守护线程)。
//自己创建的线程,而需要从一个函数开始运行(初始函数)
void myprint()
{
	cout << " 我的线程开始了" << endl;
	cout << " 我的线程执行完毕1" << endl;
	cout << " 我的线程执行完毕2" << endl;
	cout << " 我的线程执行完毕3" << endl;
	cout << " 我的线程执行完毕4" << endl;
	cout << " 我的线程执行完毕5" << endl;
	cout << " 我的线程执行完毕6" << endl;
	cout << " 我的线程执行完毕7" << endl;
	cout << " 我的线程执行完毕8" << endl;
	cout << " 我的线程执行完毕9" << endl;
	cout << " 我的线程执行完毕10" << endl;
}
int main()
{

	
	thread myobj(myprint);
	//myobj.join();
	myobj.detach();

	cout << " I love China1" << endl;
	cout << " I love China2" << endl;
	cout << " I love China3" << endl;
	cout << " I love China4" << endl;
	cout << " I love China5" << endl;
	cout << " I love China6" << endl;
	cout << " I love China7" << endl;
	cout << " I love China8" << endl;
	cout << " I love China9" << endl;
	cout << " I love China10" << endl;

	return 0;
}

C++线程运行的开始和结束(代码演示)_第2张图片
从结果来看,主线程和子线程各走各的,子线程驻留在后台运行,当主线程结束后,子线程的结果在这里无法被打印出来。

  • detach使myprint失去我们自己的控制。
    一旦调用了detach,就不能再用join了,否则会报异常。

四、joinable()
joinable():判断是否可以成功使用join()或detach();
返回true(可以join()或detach())
或false(达咩)

	thread myobj(myprint);
	if (myobj.joinable())
	{
		cout << "true!" << endl;
	}
	else
	{
		cout << "false!" << endl;
	}

	myobj.detach();

	if (myobj.joinable())
	{
		cout << "true-1!" << endl;
	}
	else
	{
		cout << "false-1!" << endl;
	}
	

C++线程运行的开始和结束(代码演示)_第3张图片
五、其他创建线程手法
1 用类对象,以及一个问题范例


class TA
{
public:
	void operator()()//重载(),不带参数
	{
		cout << " 我的线程operator()开始了" << endl;
		cout << " 我的线程operator()执行完毕" << endl;
	}

};
int main()
{
	
	TA ta;
	thread myobj(ta);//ta:可调用对象
	myobj.join();

	cout << " I love China1" << endl;
	
	return 0;
}

如果要用detach线程分离的话:

 class TA
{
public:
	int& m_i;
	TA(int& i) :m_i (i){}//构造函数
	
	void operator()()//重载(),不带参数
	{
		/*cout << " 我的线程operator()开始了" << endl;
		cout << " 我的线程operator()执行完毕" << endl;*/
		cout << "1m_i的值为:" << m_i << endl;
		cout << "2m_i的值为:" << m_i << endl;
		cout << "3m_i的值为:" << m_i << endl;
		cout << "4m_i的值为:" << m_i << endl;
		cout << "5m_i的值为:" << m_i << endl;
		cout << "6m_i的值为:" << m_i << endl;
	}

};
int main()
{
	int myi = 6;
	TA ta(myi);
	thread myobj(ta);//ta:可调用对象
	//myobj.join();
	myobj.detach();

	cout << " I love China1" << endl;
	
	return 0;
}
	

在此例中,使用的detach进行线程分离,子线程中使用的引用传递,当主线程执行完后,myi这个值就会被销毁,而子线程此时却不一定执行结束,此时就会发生不可预料的结果。
在这里插入图片描述
如果使用值传递,相当于拷贝的一份值,主线程结束也不影响子线程。
还有需要注意的是:
问:一旦调用了detach(),当主线程执行结束,这个ta对象,还在吗?
答:不在,但没关系,这个对象实际上被复制到子线程中去,执行完主线程后,复制品依然存在。
所以只要TA对象里没有引用,指针,就不会产生问题。

class TA
{
public:
	int m_i;
	TA(int i) :m_i (i)
	{
		cout << "TA()的构造函数执行" << endl;
	
	}

	TA(const TA& ta) 
	{
		m_i = ta.m_i;
		cout << "TA()的拷贝构造函数执行" << endl;
	}
	~TA()
	{
		cout << "TA()的析构函数执行" << endl;
	}
	
	void operator()()//重载(),不带参数
	{
		/*cout << " 我的线程operator()开始了" << endl;
		cout << " 我的线程operator()执行完毕" << endl;*/
		cout << "1m_i的值为:" << m_i << endl;
		cout << "2m_i的值为:" << m_i << endl;
		cout << "3m_i的值为:" << m_i << endl;
		cout << "4m_i的值为:" << m_i << endl;
		cout << "5m_i的值为:" << m_i << endl;
		cout << "6m_i的值为:" << m_i << endl;
	}

};
int main()
{
	int myi = 6;
	TA ta(myi);
	thread myobj(ta);//ta:可调用对象
	myobj.join();
	//myobj.detach();

	cout << " I love China1" << endl;
	
	return 0;
}

C++线程运行的开始和结束(代码演示)_第4张图片
从结果可以看出发生了拷贝。
第一个打印的析构为子线程的析构,第二个为主线程的析构。

2 用lambda表达式

int main()
{
	//线程入口
	auto mylambthread = [] {
		cout << " 我的线程开始了" << endl;
		cout << " 我的线程执行完毕10" << endl;
	};
	thread myobj(mylambthread);
	myobj.join();

	cout << " I love China1" << endl;
	
	return 0;
}

你可能感兴趣的:(c++,学习笔记,c++)