问题产生原因
每一个程序至少拥有一个线程,那就是执行main()函数的主线程,而多线程则是出现两个或两个以上的线程并行运行,即主线程和子线程在同一时间段同时运行。而在这个过程中会出现几种情况:
- 主线程先运行结束
- 子线程先运行结束
- 主子线程同时结束
在一些情况下需要在子线程结束后主线程才能结束,而一些情况则不需要等待,但需注意一点,并不是主线程结束了其他子线程就立即停止,其他子线程会进入后台运行.
join
join()函数是一个等待线程完成函数,主线程需要等待子线程运行结束了才可以结束:
#include <iostream>
#include <thread>
using namespace std;
void func()
{
for(int i = -10; i > -20; i--)
{
cout << "from func():" << i << endl;
}
}
int main() //主线程
{
cout << "mian()" << endl;
cout << "mian()" << endl;
cout << "mian()" << endl;
thread t(func); //子线程
t.join(); //等待子线程结束后才进入主线程
return 0;
}
运行结果:
detch
称为分离线程函数,使用detach()函数会让线程在后台运行,即说明主线程不会等待子线程运行结束才结束。
通常称分离线程为守护线程(daemon threads),UNIX中守护线程是指,没有任何显式的用户接口,并在后台运行的线程。这种线程的特点就是长时间运行;线程的生命周期可能会从某一个应用起始到结束,可能会在后台监视文件系统,还有可能对缓存进行清理,亦或对数据结构进行优化。另一方面,分离线程的另一方面只能确定线程什么时候结束,发后即忘(fire andforget)的任务就使用到线程的这种方式。
我们看下下面的这个例子:
#include <iostream>
#include <thread>
#include <windows.h>
void func(){
for (int i = 0; i < 100; ++i) {
std::cout<<"thread::func"<<std::endl;
}
}
int main() {
std::thread my_thread(func);
// Sleep(1);
my_thread.detach();
// my_thread.join();
return 0;
}
运行结果:
由于使用的是detch函数,新线程与主线程分离,在detch函数执行完成,主线程main函数结束,my_thread对象销毁,还未执行一次打印线程也就结束了,这才造成这样的结果。下面我们加上Sleep(1);这句话,观察结果,验证我们的说法:
运行结果:
我们在执行线程分离前sleep1微秒,这样线程有机会完成几次打印,结果验证了前面的说法。
如果代码变成使用join函数:
#include <iostream>
#include <thread>
#include <windows.h>
void func(){
for (int i = 0; i < 100; ++i) {
std::cout<<"thread::func"<<std::endl;
}
}
int main() {
std::thread my_thread(func);
// Sleep(1);
// my_thread.detach();
my_thread.join();
return 0;
}
结果:打印100次的thread::func。
总结
- 在一个线程中,开了另一个线程去干另一件事,使用join函数后,原始线程会等待新线程执行结束之后,再去销毁线程对象。
- 在一个线程中,开了另一个线程去干另一件事,使用join函数后,原始线程会等待新线程执行结束之后,再去销毁线程对象。
这样有什么好处?---->因为它要等到新线程执行完,再销毁,线程对象,这样如果新线程使用了共享变量,等到新线程执行完再销毁这个线程对象,不会产生异常。如果不使用join,使用detch,那么新线程就会与原线程分离,如果原线程先执行完毕,销毁线程对象及局部变量,并且新线程有共享变量或引用之类,这样新线程可能使用的变量,就变成未定义,产生异常或不可预测的错误。 - 所以,当你确定程序没有使用共享变量或引用之类的话,可以使用detch函数,分离线程。
- 但是使用join可能会造成性能损失,因为原始线程要等待新线程的完成,所以有些情况(前提是你知道这种情况,如上)使用detch会更好。