本博文由 youngpan1101 出品,转载请注明出处。
文章链接:
作者: 宋洋鹏(youngpan1101)
邮箱: yangpeng_song@163.com
ps:该博文是《王健伟老师的:c++11多线程并发视频教程》的课后笔记。
目录
线程运行的开始和结束示例
当一个程序运行起来,进程就开始执行,该进程所属的主线程也会自动开始运行。
主线程是从main()开始执行,主线程是否执行完毕决定了整个进程的执行进度,而我们另外构建的线程也需要从一个函数开始运行,一旦该函数运行完毕,意味着我们构建的线程也结束运行。若主线程执行完毕,而其他子线程还没有执行完毕,那这些子线程也会被操作系统强行终止。
结论:若想要保持子线程的运行状态,则建议让主线程一直保持运行,让主线程去等待子线程的结束。(后面接触到detach方法可以让子线程在后台运行,但是在大型项目中会有不可预料的bug,所以这里是建议)
join():加入/汇合,即阻塞主线程,让主线程等待子线程执行完毕,然后子线程和主线程汇合,然后主线程再往下执行。
#include <iostream>
#include <string>
#include <thread> //增加头文件
using namespace std;
//自己构建线程的入口函数
void myFunc()
{
cout << "我的线程开始执行了。。。" << endl;
// ...
cout << "我的线程执行完毕了。。。" << endl;
}
int main()
{
std::cout << "Hello World!\n";
thread myThreadObject(myFunc); //1)创建了线程,线程执行入口为 myFunc(); 2)myFunc线程开始执行
myThreadObject.join(); // join():加入/汇合,即阻塞主线程,让主线程等待子线程执行完毕,然后子线程和主线程汇合,然后主线程再往下执行
cout << "主线程安全退出!!!" << endl;
return 0;
}
上面demo中如果主线程执行完毕了,但子线程没执行完毕,这种程序是不稳定的,所以用了 join() 来阻塞主线程并等待myFunc子线程执行完毕。
若将 join() 函数注释掉,运行会报异常,因为主线程执行完毕后子线程还在运行中。
detach():分离,主线程和子线程是相互分离的状态,不存在等待谁执行完毕的说法。一旦detach()之后,与该主线程关联的thread对象就会失去与主线程的关联,该子线程会驻留在后台运行,也就是说detach()会使线程myFunc失去控制。
另外,若调用了detach(),就不能用join(),否则系统会报异常。
#include <iostream>
#include <string>
#include <thread> //增加头文件
using namespace std;
//自己构建线程的入口函数
void myFunc()
{
cout << "我的线程开始执行了。。。" << endl;
// ...
cout << "我的线程执行完毕了。。。" << endl;
}
int main()
{
std::cout << "Hello World!\n";
thread myThreadObject(myFunc); //1)创建了线程,线程执行入口为 myFunc(); 2)myFunc线程开始执行
//myThreadObject.join(); // join():加入/汇合,即阻塞主线程
myThreadObject.detach();
cout << "主线程安全退出!!!1" << endl;
cout << "主线程安全退出!!!2" << endl;
cout << "主线程安全退出!!!3" << endl;
cout << "主线程安全退出!!!4" << endl;
cout << "主线程安全退出!!!5" << endl;
cout << "主线程安全退出!!!6" << endl;
cout << "主线程安全退出!!!7" << endl;
cout << "主线程安全退出!!!1" << endl;
cout << "主线程安全退出!!!2" << endl;
cout << "主线程安全退出!!!3" << endl;
cout << "主线程安全退出!!!4" << endl;
cout << "主线程安全退出!!!5" << endl;
cout << "主线程安全退出!!!6" << endl;
cout << "主线程安全退出!!!7" << endl;
return 0;
}
joinable():判断该线程是否为可执行线程。
通常以下几种情况会导致线程成为 not-joinable:
- 由 thread 的缺省构造函数构造而成( thread() 没有参数);
- 该 thread 被 move 过(包括 move 构造和 move 赋值);
- 该线程调用过 join 或者 detach。
#include <iostream>
#include <string>
#include <thread> //增加头文件
using namespace std;
//自己构建线程的入口函数
void myFunc()
{
cout << "我的线程开始执行了。。。" << endl;
// ...
cout << "我的线程执行完毕了。。。" << endl;
}
int main()
{
thread nonParamThread; //thread()没有参数
thread myThreadObject(myFunc); //1)创建了线程,线程执行入口为 myFunc(); 2)myFunc线程开始执行
//myThreadObject.join(); // join():加入/汇合,即阻塞主线程
cout << "Joinable after construction:" << endl;
cout << "nonParamThread: " << nonParamThread.joinable() << endl;
cout << "myThreadObject: " << myThreadObject.joinable() << endl;
if (nonParamThread.joinable())
nonParamThread.detach();
if (myThreadObject.joinable())
myThreadObject.detach();
cout << "Joinable after joining:" << endl;
cout << "nonParamThread: " << nonParamThread.joinable() << endl;
cout << "myThreadObject: " << myThreadObject.joinable() << endl;
return 0;
}
其他创建线程的方法
类对象_创建线程
#include <iostream>
#include <string>
#include <thread> //增加头文件
using namespace std;
class myClass
{
public:
void operator()() //重载,不能带参数
{
cout << "我的线程 myClass::operator() 开始执行了。。。" << endl;
//...
cout << "我的线程 myClass::operator() 结束执行了。。。" << endl;
}
};
int main()
{
myClass myClassObj;
thread myThreadObj(myClassObj);
myThreadObj.join(); //等待子线程执行结束
cout << "主线程安全退出了!!!" << endl;
return 0;
}
C/C++ - 类中成员变量是引用 【Link】
若类对象的方法创建线程中,是通过引用来传参,而又用detach()函数,则会出现主线程提前结束,会回收局部变量,而myClass::m_i 又在被读取,最终导致意想不到的异常(!!!这里也是重申尽量不要用detach()!!!),具体代码如下:
#include <iostream>
#include <string>
#include <thread> //增加头文件
using namespace std;
class myClass
{
public:
int& m_i; //成员变量是引用
myClass(int& i) :m_i(i)
{
cout << "myClass 构造函数执行了..." << endl;
}
myClass(const myClass& myclass) :m_i(myclass.m_i)
{
cout << "myClass 拷贝构造函数执行了..." << endl;
}
~myClass()
{
cout << "~myClass 析构函数执行了..." << endl;
}
void operator()() //不能带参数
{
//cout << "我的线程 myClass::operator() 开始执行了。。。" << endl;
//...
//cout << "我的线程 myClass::operator() 结束执行了。。。" << endl;
cout << "m_i = " << m_i << endl;
}
};
int main()
{
int myIntVal = 8; //myIntVal是局部变量,主线程接受后,该变量会被回收
myClass myClassObj(myIntVal);
//myClassObj会被复制到线程中,当主线程被销毁,myThreadObj还是可以正常运行,但是不能包括引用、指针等传参
thread myThreadObj(myClassObj);
//myThreadObj.join(); //等待子线程执行结束
myThreadObj.detach(); //子线程和主线程分离
for (int i = 0; i < 24; i++)
cout << "主线程安全退出了!!!" << i << endl;
return 0;
}
lambda表达式_创建线程
int main()
{
//...
auto mylamThread = [] {
cout << "我的线程 mylamThread 开始执行了。。。" << endl;
//...
cout << "我的线程 mylamThread 结束执行了。。。" << endl;
};
thread myThreadObj3(mylamThread);
myThreadObj3.join();
}