文章目录
1、线程创建方法及案例
- 程序运行起来,生成一个进程,该进程所属的主线程开始自动运行;当主线程从
main()
函数返回,则整个进程执行完毕。 - 主线程从
main()
开始执行,那么我们自己创建的线程,也需要从一个函数开始运行(初始函数),一旦这个函数运行完毕,线程也结束运行。 - 整个进程是否执行完毕的标志是:主线程是否执行完,如果主线程执行完毕了,就代表整个进程执行完毕了,此时如果其他子线程还没有执行完,也会被强行终止。所以,一般情况下,如果想保持子线程的运行状态的话,那么大家要让主线程一直保持运行,不要让主线程运行完毕。【此条有例外,以后会解释】
1.1 范例
创建一个线程:
- 包含头文件thread ->
#include <thread>
- 写初始函数 -> void myprint() {}
- 在main中创建thread -> thread mytobj(myprint);
#include <iostream>
#include <thread>
using namespace std;
void myprint()
{
cout << "我的线程开始执行了" << endl;
/*
......
*/
cout << "我的线程执行完毕了" << endl;
}
//必须明确一点:这个程序有两个线程在跑。相当于整个程序的执行有两条线在同时走,即使一条线被堵住了,另外一条线还是可以通行的,这就是多线程。
int main()
{
thread mytobj(myprint);
mytobj.join();
cout << "主线程收尾,最终主线程安全正常退出!" << endl;
system("pause");
return 0;
}
1.2 thread
thread
:是个标准库的类!
void myprint() {}
int main()
{
//创建一个线程对象,myprint为可调用对象
/*这行代码含义:Ⅰ.创建了线程,线程执行起点(入口)是myprint这个函数(可调用对象);
Ⅱ.myprint线程开始执行 */
thread mytobj(myprint);
}
1.3 join()
join()
:加入/汇合,说白了就是阻塞,阻塞主线程,让主线程等待子线程执行完毕,然后子线程和主线程汇合,然后主线程再往下走。
void myprint() {}
int main()
{
thread mytobj(myprint);
mytobj.join();//主线程阻塞到这里等待myprint()执行完,当子线程执行完毕,这个join()就执行完毕,主线程就继续往下走
cout << "主线程收尾,最终主线程安全正常退出!" << endl;
return 0;
}
注意:如果主线程执行完毕了,但子线程没执行完毕且不能继续执行,这种程序是不合格的!
一个书写良好的程序,大部分情况下应该是主线程等待子线程执行完毕后,自己才能最终退出!
1.4 detach()
detach()
:分离,主线程不再和子线程汇合,主线程和子线程各自执行,主线程也不必等待子线程运行结束,主线程可以先执行结束,这并不影响子线程的执行。
为什么引入detach() ?
**答:**当创建了很多子线程时,让主线程逐个等待子线程结束,这种编程方法不太好,所以引入了
detach()
。
- 一旦引入
detach()
之后,与这个主线程关联的thread
对象就会失去与这个主线程的关联,此时这个子线程就会驻留后台运行(此时主线程与子线程已经失去联系),这个子线程就会被C++运行时库接管,当这个子线程执行完成后,由运行时库负责清理该线程相关的资源(守护线程)
void myprint() {}
int main()
{
thread mytobj(myprint);
mytobj.detach();
cout << "I love China!" << endl;
return 0;
}
detach()
使线程myprint失去我们的控制。
注意:一旦调用了detach()
,就不能再使用join()
,否则系统会报告异常。
1.5 joinable()
joinable():
判断是否可以成功使用join()
或者detach()
,可以使用join()
或者detach()
,则返回 true,否则,返回 false。
#include <iostream>
#include <thread>
using namespace std;
void myprint() {}
int main()
{
thread mytobj(myprint);
if (mytobj.joinable())
{
cout << "1.joinable() == true" << endl; //√
}
else
{
cout << "1.joinable() == false" << endl;
}
mytobj.detach();
if (mytobj.joinable())
{
cout << "2.joinable() == true" << endl;
}
else
{
cout << "2.joinable() == false" << endl; //√
}
cout << "I love China" << endl;
return 0;
}
2、其他创建线程的手法
2.1 、用类对象
- 用类对象创建线程时,类内必须要重载
()
,使类对象成为可调用对象。
#include <iostream>
#include <thread>
using namespace std;
class TA
{
public:
void operator()() //不能带参数
{
cout << "我的线程operator()开始执行了" << endl;
/*
......
*/
cout << "我的线程operator()执行完毕了" << endl;
}
};
int main()
{
TA ta;
thread mytobj(ta); //ta:可调用对象
mytobj.join(); //等待子线程执行结束
cout << "I love China 1" << endl;
cout << "I love China 2" << endl;
cout << "I love China 3" << endl;
cout << "I love China 4" << endl;
cout << "I love China 5" << endl;
system("pause");
return 0;
}
当使用
detach()
时,要注意,类内创建类变量时不能使用变量的引用,当在主线程中创建变量并赋给类内变量时,传递的是主线程中变量的内存地址,当主线程结束后,主线程中变量的内存就会被释放,主线程中变量也不复存在,这样的话,子线程访问到的内存地址可能会被其他程序所占领,所指向的值也可能会被修改,这样子线程就会出现不可预料的结果,所以应使用值传递。
-
如下案例:
#include <iostream> #include <thread> using namespace std; class TA { public: //int& m_i; int m_i; //正确用法,使用值传递 //TA(int& i) :m_i(i) {} TA(int i) :m_i(i) {} //正确用法,使用值传递 TA(const TA& ta) :m_i(ta.m_i) { cout << "TA()拷贝构造函数被执行" << endl; } ~TA() { cout << "~TA()析构函数被执行" << endl; } void operator()() //不能带参数 { cout << "m_i 1 的值为:" << m_i << endl; cout << "m_i 2 的值为:" << m_i << endl; cout << "m_i 3 的值为:" << m_i << endl; cout << "m_i 4 的值为:" << m_i << endl; cout << "m_i 5 的值为:" << m_i << endl; } }; int main() { TA ta; thread mytobj(ta); //ta:可调用对象 mytobj.join(); //mytobj.detach(); cout << "I love China 1" << endl; cout << "I love China 2" << endl; cout << "I love China 3" << endl; cout << "I love China 4" << endl; cout << "I love China 5" << endl; system("pause"); return 0; }
问题:一旦调用了
detach()
,当主线程结束了,子线程用的 ta(类对象)这个对象还在吗?**答:**ta 这个对象确实不在了,但是创建子线程时,这个对象实际上是被复制到子线程中去的,执行完主线程后,ta会被销毁,但是所复制的TA对象依旧存在。
-
使用
join()
来证明以上问题,程序执行结果如下:
2.2、 用lambda表达式
#include <iostream>
#include <thread>
using namespace std;
int main()
{
auto mylamthread = []
{
cout << "我的线程开始执行了" << endl;
/*
......
*/
cout << "我的线程执行结束了" << endl;
};
thread mytobj(mylamthread);
mytobj.join();
//mytobj.detach();
cout << "I love China 1" << endl;
cout << "I love China 2" << endl;
cout << "I love China 3" << endl;
cout << "I love China 4" << endl;
cout << "I love China 5" << endl;
return 0;
}
注:本人学习c++多线程视频地址:C++多线程学习地址