2.1 范例演示线程运行的开始和结束
1、程序运行起来,生成一个进程,该进程所属的主线程开始自动运行
实际上这个是主线程在执行,主线程从main()函数返回,则整个进程执行完毕
2、主线程从main ()开始执行,那么我们自己创建的线程,也需要从一个函数开始运行(初始函数),一旦这个函数运行完毕,就代表着我们这个线程运行结束
3、整个进程是否执行完毕的标志是主线程是否执行完,如果主线程执行完毕了,就代表整个进程执行完毕了,此时,一般情况下如果其他子线程还没有执行完毕,那么这些子线程也会被操作系统强行终止。所以,一般情况下,我们得到一个结论:如果大家想保持子线程(自己用代码创建的线程)的运行状态的话,那么大家要让主线程一直保持运行,不要让主线程运行完毕
4、创建多线程的方法
(1)包含了thread头文件
(2)初始函数要写
(3)main中开始写代码
大家必须明确一点:有两个线程在跑,相当整个程序的执行有两条线在同时走,所以,可以同时干两个事,即使一条线被堵住了,另外一条线还是可以通行的,这就是多线程
2.1.1 thread
是个标准库里的类
2.1.2 join()
加入/汇合,说白了就是阻塞,阻塞主线程,让主线程等待子线程执行完毕,然后子线程和主线程汇合,然后主线程再往下走。如果主线程执行完毕了,但子线程没执行完毕,这种程序员是不合格的,写出来的程序也是不稳定的,一个书写良好的程序,应该是主线程等待子线程执行完毕后自己才能最终退出
#include <iostream>
#include <thread>
using namespace std;
//自己创建的线程也要从一个函数(初始函数)开始运行
void myPrint()
{
cout << "子线程工作1" << endl;
cout << "子线程工作2" << endl;
cout << "子线程工作3" << endl;
}
int main()
{
//myPrint可调用对象
thread myThreadObj(myPrint); //(1)创建了线程,线程执行起点(入口)是myPrint(); (2)myPrint线程开始执行
//阻塞主线程并等待myPrint子线程执行完毕
myThreadObj.join(); //主线程阻塞到这里等待myPrint()执行完,当子线程执行完毕,这个join()就执行完毕,主线程就继续往下走
cout << "主线程收尾,最终主线程安全正常退出" << endl;
system("pause");
return 0;
}
拓展:两个子线程同时执行,此时都需要用join阻塞主线程
#include <iostream>
#include <thread>
using namespace std;
//自己创建的线程也要从一个函数(初始函数)开始运行
//子线程1
void myPrint1()
{
cout << "子线程1工作1" << endl;
cout << "子线程1工作2" << endl;
cout << "子线程1工作3" << endl;
}
//子线程2
void myPrint2()
{
cout << "子线程2工作1" << endl;
cout << "子线程2工作2" << endl;
cout << "子线程2工作3" << endl;
}
int main()
{
//myPrint可调用对象
thread myThread1Obj(myPrint1); //(1)创建了线程,线程执行起点(入口)是myPrint1(); (2)myPrint1线程开始执行
thread myThread2Obj(myPrint2); //(2)创建了线程,线程执行起点(入口)是myPrint2(); (2)myPrint2线程开始执行
//阻塞主线程并等待子线程执行完毕
myThread1Obj.join(); //主线程阻塞到这里等待myPrint1()执行完,当子线程执行完毕,这个join()就执行完毕,主线程就继续往下走
myThread2Obj.join(); //主线程阻塞到这里等待myPrint2()执行完,当子线程执行完毕,这个join()就执行完毕,主线程就继续往下走
cout << "主线程收尾,最终主线程安全正常退出" << endl;
system("pause");
return 0;
}
2.1.3 detach()
传统多线程程序主线程要等待子线程执行完毕,然后自己再最后退出,而detach()打破了这种规则,detach分离的意思,也就是主线统程不和子线程汇合了,你主线程执行你的,我子线程执行我的,你主线程也不必等我子线程运,你可以先执行结束,这并不影响我子线程的执行
为什么引入detach(),我们创建了很多子线线程,让主线程逐个等待子线程结束,这种编程方法不太好,所以引入了detach(),好比之前拓展里的写法,就是主线程逐个等待子线程。有的老师说还是建议让主线程逐个等待子线程,以免造成不必要的错误
一旦detach()之后,与这个主线程关联的thread对象就会失去与这个主线程的关联,此时这个子线程就会驻留在后台运行(主线程跟该子线程失去联系)
这个子线程就相当于被c++运行时库接管,当这个子线程执行完成后,由运行时库负责清理该线程相关的资源(守护线程)
detach()使子线程失去我们自己的控制。一旦调用了detach(),就不能再用join),否则系统会报告异常。
#include <iostream>
#include <thread>
using namespace std;
//自己创建的线程也要从一个函数(初始函数)开始运行
void myPrint()
{
cout << "子线程工作1" << endl;
cout << "子线程工作2" << endl;
cout << "子线程工作3" << endl;
}
int main()
{
//myPrint可调用对象
thread myThreadObj(myPrint); //(1)创建了线程,线程执行起点(入口)是myPrint(); (2)myPrint线程开始执行
//分离主线程和子线程
myThreadObj.detach();
cout << "主线程工作1" << endl;
cout << "主线程工作2" << endl;
cout << "主线程工作3" << endl;
system("pause");
return 0;
}
2.1.4 joinable()
joinable():判断是否可以成功使用join() 或者 detach()的;返回true(可以join或者detach)或者false(不能join或detach)
#include <iostream>
#include <thread>
using namespace std;
//自己创建的线程也要从一个函数(初始函数)开始运行
void myPrint()
{
cout << "子线程工作1" << endl;
cout << "子线程工作2" << endl;
cout << "子线程工作3" << endl;
}
int main()
{
//myPrint可调用对象
thread myThreadObj(myPrint); //(1)创建了线程,线程执行起点(入口)是myPrint(); (2)myPrint线程开始执行
if (myThreadObj.joinable())
{
cout << "1:joinable == true" << endl;
}
else
{
cout << "1:joinable == false" << endl;
}
myThreadObj.detach();
if (myThreadObj.joinable())
{
cout << "2:joinable == true" << endl;
}
else
{
cout << "2:joinable == false" << endl;
}
cout << "主线程工作1" << endl;
system("pause");
return 0;
}
#include <iostream>
#include <thread>
using namespace std;
//自己创建的线程也要从一个函数(初始函数)开始运行
void myPrint()
{
cout << "子线程1工作1" << endl;
cout << "子线程1工作2" << endl;
cout << "子线程1工作3" << endl;
}
int main()
{
thread myThreadObj(myPrint); //(1)创建了线程,线程执行起点(入口)是myPrint(); (2)myPrint线程开始执行
if (myThreadObj.joinable())
{
myThreadObj.join();
}
cout << "主线程工作" << endl;
system("pause");
return 0;
}
2.2 其它创建线程的手法
2.2.1 用类对象(可调用对象)创建线程
#include <iostream>
#include <thread>
using namespace std;
class TA
{
public:
void operator()() //不能带参数
{
cout << "我的线程开始执行了" << endl;
cout << "我的线程执行完毕了" << endl;
}
};
int main()
{
TA ta;
thread myThreadObj(ta); //ta可调用对象
myThreadObj.join(); //等待子线程执行结束
cout << "主线程执行结束!!!" << endl;
system("pause");
return 0;
}
大家可能还有一个疑问:一旦调用了detach(),那我主线线程执行结束了,我这里用的这个ta这个对象还在吗?(对象不在了)
这个对象实际上是被复制到线程中去,执行完主线程后,ta会被销毁,但是所复制的TA对象依旧存在。所以,只要你这个TA类对象里没有引用,没有指针,那么就不会产生问题
#include <iostream>
#include <thread>
using namespace std;
class TA
{
public:
TA()
{
cout << "构造函数开始执行" << endl;
}
TA(const TA &ta)
{
cout << "拷贝构造函数开始执行" << endl;
}
~TA()
{
cout << "析构函数开始执行" << endl;
}
void operator()() //不能带参数
{
cout << "我的子线程开始执行了" << endl;
cout << "我的子线程执行完毕了" << endl;
}
};
int main()
{
TA ta;
thread myThreadObj(ta); //ta可调用对象
//myThreadObj.join(); //等待子线程执行结束
myThreadObj.detach();
cout << "主线程执行结束!!!" << endl;
return 0;
}
2.2.2 用lambda表达式创建线程
#include <iostream>
#include <thread>
using namespace std;
int main()
{
//lambda表达式
auto myLambdaThread = [] {
cout << "我的子线程开始执行" << endl;
cout << "我的子线程执行结束" << endl;
};
thread myThreadObj(myLambdaThread);
myThreadObj.join();
cout << "主线程执行结束!!!" << endl;
return 0;
}