线程启动、结束,创建线程多法、join,detach
范例演示线程运行的开始和结束
- 程序运行起来,生成一个进程,该进程所属的主线程开始自动运行。
- 主线程从main函数开始执行,那么我们自己创建的线程, 也需要从一个函数开始运行(初始函数),一旦这个函数运行完毕,就代表着我们这个线程运行结束(类似main函数)。
- 整个进程是否执行完毕的标志是主线程是否执行完,如果主线程执行完毕了,就代表整个进程执行完毕了。
- 此时,一般情况下:如果其他子线程还没有执行完毕,那么这些子线程也会被操作系统强行终止。
- 所以,一般情况下,我们得到一个结论:如果我们想保持子线程(自己用代码创建的线程)的运行状态的话,那么就要让主线程一直保持运行,不要让主线程运行完毕。
- 包含头文件 #include<thread>
代码语言:javascript
复制#include <iostream>
#include <string>
#include <thread>
using namespace std;
//自己创建的线程也要从一个函数(初始函数)开始执行
void myprint() {
cout << "我的线程开始执行了" << endl;
//...
//...
cout << "我的线程结束运行了" << endl;
}
int main()
{
thread mythread(myprint);
mythread.join();
//主线程执行
std::cout << "Main Thread" << std::endl;
return 0;
}
- 有两个线程在跑,相当整 个程序的执行有两条线在同时走, 所以,可以同时干两个事, 即使一条线被堵住了,另外一条线还是可以通行的。
thread
代码语言:javascript
复制thread mythread(myprint);
- myprint是可调用对象。
- 这句代码干了两件事:
- 创建了线程,线程执行起点(入口)myprint()。
- myprint线程开始执行。
join()
- 加入/汇合,说白了就是阻塞,阻塞主线程,让主线程等待子线程执行完毕,然后子线程和主线程汇合,然后再往下走。
代码语言:javascript
复制//阻塞主线程并等待子线程执行完mythread.join();
- 主线程阻塞到这里等待myprint()执行完,当子线程执行完毕,这个join()就执行完毕,主线程就续往下走。
- 注释掉 join() 语句,观察现象:
- 如果主线程执行完毕了,但子线程没执行完毕,这种程序是不合格的,程序是不稳定的。
- 一个书写良好的程序,应该是主线程等待子线程执行完毕后,自己才能最终退出。
detach()
代码语言:javascript
复制mythread.detach();
- detac():传统多线程程序主线程要等待子线程执行完毕,然后自己再最后退出.
- detach:分离,也就是主线程不和子线程汇合了,你主线程执行你的,我子线程执行我的,你主线程也不必等我子线程运行完毕,我子线程也不用等你主线程运行完毕。
- 为什么引入detach():我们创建了很多子线程,让主线程逐个等待子线程结束,这种编程方法不太好,所以引入了detach)。
- 一旦detach()之后,与这个主线程关联的thread对象就会失去与这个主线程的关联。此时这个子线程就会理留在后台运行(主线程与跟该子线程关去联系)。
- 这个子线程就相当于被c 运行时库接管,当这个子线程执行完成后,由运行时库负责清理该线程相关的资源(守护线程)。
- detach() 使线程myprint失去我们的控制。
- 一旦调用 了detach(), 就不能再用join(),否则系统会报告异常。
joinable()
- 判断是否可以成功使用join (或者detach ()的。
- 返回true (可以join或者detach)
- false(不能join或者detach)。
代码语言:javascript
复制#include <iostream>
#include <string>
#include <thread>
using namespace std;
//自己创建的线程也要从一个函数(初始函数)开始执行
void myprint() {
cout << "我的线程开始执行了" << endl;
//...
//...
cout << "我的线程结束运行了" << endl;
}
int main()
{
thread mythread(myprint);
if (mythread.joinable()) {
cout << "1: true " << endl;
}
else {
cout << "1: false " << endl;
}
mythread.detach();
if (mythread.joinable()) {
cout << "2: true " << endl;
}
else {
cout << "2: false " << endl;
}
//主线程执行
std::cout << "主线程收尾" << std::endl;
return 0;
}
其他创建线程的手法
用类对象(可调用对象),以及一个问题范例
代码语言:javascript
复制#include <iostream>
#include <string>
#include <thread>
using namespace std;
class AE
{
public:
void operator()(){
cout << "我的线程operator()开始执行了" << endl;
//...
//...
cout << "我的线程operator()执行结束了" << endl;
}
};
int main()
{
AE ae;
thread mythread(ae); //ae: 可调用对象
mythread.join();
//主线程执行
std::cout << "主线程收尾" << std::endl;
return 0;
}
代码语言:javascript
复制#include <iostream>
#include <string>
#include <thread>
using namespace std;
class AE
{
int& m_i;
public:
AE(int& i):m_i(i){}
void operator()(){
cout << "m_i的值"<< m_i << endl;
}
};
int main()
{
int myi = 69;
AE ae(myi);
thread mythread(ae); //ae: 可调用对象
mythread.detach();
//主线程执行
std::cout << "主线程收尾" << std::endl;
return 0;
}
- 另一个疑问:一旦调用了detach(), 那我主线程执行结束了,我这里用的这个ae这个对象还在吗? (对象不在了)
- 这个对象实际上是被复制(值拷贝方式)到线程中去,执行完主线程后,ae会被销毁,但是所复制的AE对象依旧存在。
- 所以,只要这个AE类对象里没有引用,没有指针,那么就不会产生问题;。
- 证明:
代码语言:javascript
复制#include <iostream>
#include <string>
#include <thread>
using namespace std;
class AE
{
int& m_i;
public:
AE(int& i):m_i(i){
cout << "AE 构造函数执行了" << endl;
}
AE(const AE& other):m_i(other.m_i) {
cout << "AE 拷贝构造函数执行了" << endl;
}
~AE() {
cout << "~AE 析构函数执行了" << endl;
}
void operator()(){
cout << "m_i的值"<< m_i << endl;
}
};
int main()
{
int myi = 69;
AE ae(myi);
thread mythread(ae); //ae: 可调用对象
mythread.detach();
//主线程执行
std::cout << "主线程收尾" << std::endl;
return 0;
}
用lambda表达式
代码语言:javascript
复制#include <iostream>
#include <string>
#include <thread>
using namespace std;
int main()
{
auto lambda1 = []() {
cout << "lambda1线程执行了" << endl;
//...
cout << "lambda1线程结束了" << endl;
};
thread mythread(lambda1); //lambda1: 可调用对象
mythread.join();
//主线程执行
std::cout << "主线程收尾" << std::endl;
return 0;
}
代码语言:javascript
复制#include <iostream>
#include <string>
#include <thread>
using namespace std;
int main()
{
auto lambda1 = []() {
cout << "lambda1线程执行了" << endl;
//...
cout << "lambda1线程结束了" << endl;
};
thread mythread([](){
cout << "lambda表达式线程执行了" << endl;
//...
cout << "lambda表达式线程结束了" << endl;
}
); //lambda表达式: 可调用对象
mythread.join();
//主线程执行
std::cout << "主线程收尾" << std::endl;
return 0;
}