C++11并发与多线程(2)线程启动、结束,创建线程多法、join、detach

(1)范例演示线程运行的开始和结束

  • 程序运行起来,生成一个进程,该进程所属的主线程开始自动运行实际上这个是主线程执行,主线程从main()函数返回,则整个进程执行完毕
  • 主线程从main()函数开始执行,那么我们自己创建的线程,也需要从一个函数开始运行(初始函数),一旦这个函数运行完毕,就代表我们这个线程运行结束)
  • 整个进程执行完毕的标志是主线程执行完毕,主线程执行完毕代表整个进程执行完毕,此时如果其他子线程还没有执行完毕,一般情况下这些子线程将会被强行终止。
  • 所以一般情况下,如果大家想保持子线程运行状态的话(自己写的线程),要让主线程一直保持运行。( 这条规矩有例外)
  • 创建线程:
    a) 包含头文件thread
    b) 初始函数要写
    c) main中开始写代码
    d) 有两个线程在跑,相当于整个程序的执行有两条线在同时走,所以,可以同时干两个事,即使一条线被堵住了,另外一条线也是可以通行的,这就是多线程。

(1.1)thread

thread是标准库中的类,myprint是可调用对象

(1.2)join()

  • 阻塞,阻塞主线程,让主线程等待子线程执行完毕,然后子线程和主线程汇合
  • 如果主线程执行完毕但子线程没执行完毕,程序不稳定,这种写法是不合格的。
  • 一个书写良好的程序,应该是主线程等待子线程执行完毕后,自己才能最终退出。

(1.3)detach()

  • 传统的多线程程序主线程要等待子线程执行完毕,然后自己再退出
  • detach:分离,主线程不和子线程汇合,主线程不必等子线程,不影响子线程的执行。
  • 为什么引入detach():我们创建了很多子线程,让主线程逐个等待子线程结束,这种编程方法不太好,所以引入了detach
  • 一旦detach()之后,与主线程关联的thread对象就会失去与这个主线程的关联,此时这个子线程就会驻留在后台运行,这个子线程就相当于被C++运行时库接管,当这个子线程执行完成之后,由运行时库负责清理该线程相关的资源。(守护线程)
  • detach()使线程myprint失去我们自己的控制。

(1.4)joinable()

判断是否可以成功使用join()或者detach();返回true(可以用join或者detach)或者false(不能使用join或者detach)

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

void myPrint()
{
	cout << "我的线程开始运行" << endl;
	//-------------
	//-------------
	cout << "我的线程运行完毕" << endl;
	return;
}

int main()
{
	//(1)创建了线程,线程执行起点(入口)是myPrint;(2)执行线程
	thread myThread(myPrint);

	//(2)阻塞主线程并等待myPrint执行完,当myPrint执行完毕,join()就执行完毕,主线程继续往下执行
	//join意为汇合,子线程和主线程回合
	myThread.join();

	//设置断点可看到主线程等待子线程的过程
	//F11逐语句,就是每次执行一行语句,如果碰到函数调用,它就会进入到函数里面
	//F10逐过程,碰到函数时,不进入函数,把函数调用当成一条语句执行

	//(3)传统多线程程序中,主线程要等待子线程执行完毕,然后自己才能向下执行
	//detach:分离,主线程不再与子线程汇合,不再等待子线程
	//detach后,子线程和主线程失去关联,驻留在后台,由C++运行时库接管
	//myThread.detach();

	//(4)joinable()判断是否可以成功使用join()或者detach()
	//如果返回true,证明可以调用join()或者detach()
	//如果返回false,证明调用过join()或者detach(),join()和detach()都不能再调用了
	if (myThread.joinable())
	{
		cout << "可以调用可以调用join()或者detach()" << endl;
	}
	else
	{
		cout << "不能调用可以调用join()或者detach()" << endl;
	}
	
	cout << "Hello World!" << endl;
	return 0;
}

(2)其他创建线程的手法

C++中的可调用对象可以是函数、函数指针、lambda表达式、bind创建的对象或者重载了函数调用运算符的类对象。

(2.1)用类,可调用对象,以及一个问题范例

  • 大家可能还有一个疑问:一旦调用了detach(),那我主线程执行结束了,我这里用的这个ta对象还在嘛?
  • 解释(对象不在了),原因:这个对象实际上是被 复制 到线程中去;执行完主线程后,ta会被销毁,但是所复制的TA对象依旧存在。所以,只要你这个TA对象里没有引用、没有指针,那么就不会产生问题。
//创建一个类,并编写圆括号重载函数,初始化一个该类的对象,把该对象作为线程入口地址
class Ta
{
public:
	void operator()() //不能带参数
	{
		cout << "我的线程开始运行" << endl;
		//-------------
		//-------------
		cout << "我的线程运行完毕" << endl;
	}
};

//main函数里的:
	Ta ta;
	thread myThread(ta);
	myThread.join();

(2.2)用lambda表达式

//main函数中
auto lambdaThread = [] {
		cout << "我的线程开始执行了" << endl;
		//-------------
		//-------------
		cout << "我的线程开始执行了" << endl;
	};

	thread myThread(lambdaThread);
	myThread.join();

(2.3)把某个类中的某个函数作为线程的入口地址

class Data_
{
public:
    void GetMsg(){}
    void SaveMsh(){}
};
//main函数里
    Data_ s;
    //第一个&意思是取址,第二个&意思是引用,相当于std::ref(s)
    //thread oneobj(&Data_::SaveMsh,s)传值也是可以的
    //在其他的构造函数中&obj是不会代表引用的,会被当成取地址
    thread oneobj(&Data_::SaveMsh,&s);
    thread twoobj(&Data_::GetMsg,&s);
    oneobj.join();
    twoobj.join();


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值