一、使用函数创建线程
1、thread()
创建一个线程入口函数,子线程从这个函数开始运行,函数结束,线程也就结束了。主线程执行完毕,代表整个进程执行完毕。
如果子线程还没执行完毕,主线程先执行完了,一般情况下,这些子线程会被强行终止,这时使用join()函数,使主线程等待子线程执行完毕后,主线程与子线程会汇合,然后主线程继续走下去。
1.#include <iostream> 2.#include <string> 3.#include <thread> //线程 4.using namespace std; 5. 6.//创建的线程,也就是一个初始函数 7.void myprint(){ 8. cout<<"1 begin" <<endl; 9. cout<<"2 end"<<endl; 10.} 11. 12.int main(){ 13. thread myobj(myprint);//创建线程,线程就已经开始执行了 14. myobj.join();//阻塞主线程,让主线程等子线程执行完,然后主线程继续往下走 15. //如果不写join,可能main先执行完了,子线程还没执行完,会报错 16. cout<<"main end"<<endl; 17. return 0; 18.}
2、detach
分离,主线程不用等子线程了,主线程执行主线程的,子线程执行子线程的,不汇合了。(一般不用detach)
原因:创建了很多子线程,让主线程逐个等待子线程结束不太好,就提出了detach。
一旦thread对象调用了detach函数,这个thread对象就失去了与主线程的联系,子线程就会驻留在后台运行,由c++运行时库接管,子线程结束后,运行时库负责清理线程相关的资源(守护线程)。
1 1.#include <iostream> 2 2.#include <thread> //线程 3 3.using namespace std; 4 4. 5 5.//创建的线程,也就是一个初始函数 6 6.void myprint(){ 7 7. cout<<"1 begin" <<endl; 8 8. cout<<"2 end"<<endl; 9 9.} 10 10. 11 11.int main(){ 12 12. thread myobj(myprint);//创建线程,线程就已经开始执行了 13 13. myobj.detach(); //主线程和子线程分离,各走各的。 14 14. cout<<"main end"<<endl; 15 15. return 0; 16 16.}
一旦用了detach,就不能再用join了!
3、joinable()
thread对象调用joinable()函数,判断是否可以成功使用join或detach,返回true或false。
一般可以用在使用join或detach之前,用来提前先判断,如果true,可以使用,否则不能使用。
二、用类创建线程
thread参数除了函数名,还可以是类对象来作为线程入口。
用类对象创建线程,这个对象实际上是被默认复制到线程中的(也就是在创建thread对象时,这个对象已经创了一个副本,在子线程中的是副本而不是本身),执行完主线程后,这个类对象会被回收,但是副本在子线程中,直到子线程结束,这个副本才会被回收,只要你这个类对象里面没有引用、指针,就不会产生问题。
当类对象里面有引用时:
class A{ public: int& m_i; A(int& i):m_i(i){}//构造函数 void operator()(){//可以实现将对象当函数来使用 cout<<"m_i的值为"<<m_i<<endl; } } int main(){ int i=9; A a(i); thread thread1(a);//a是类对象,用类对象创建子线程 //thread1.join();用join没问题 thread1.detach();//会出问题! cout<<"main end"<<endl; return 0; }
用detach的话,主线程与子线程分离了,有可能出现一种情况:
- 主线程先执行完,那么a是属于主线程栈空间的,此时a被回收。但由于A构造函数是引用,所以指向了一个已经销毁的内存,类似于野指针,会出错。
而使用join(),主线程会等子线程执行完在走,所以不会出现这个问题。
改成join后,程序流程为:
- 创建类对象ta,构造函数被执行;
- 创建子线程的同时就开始运行了,a对象副本给了子线程,副本构造函数执行,引用的主线程里的i;
- 堵塞主线程,等子线程运行完;
- 子线程结束,a对象副本执行析构函数,子线程释放内存;
- 主线程继续执行;
- main函数代码跑完了,主线程执行完毕,a对象析构函数执行,释放内存和变量a;
三、用lambda表达式
1 #include <iostream> 2 #include <thread> //线程 3 using namespace std; 4 5 int main(){ 6 auto mythread = []{ 7 cout<<"我的子线程开始了"<<endl; 8 cout<< "结束了"<<endl; 9 }; 10 thread myobj(mythread); 11 myobj.join(); 12 cout<<"main end"<<endl; 13 return 0; 14 }
TA构造函数执行,创建线程myobj,这个ta对象时复制进去的,子线程中使用的是副本。