目录
铺垫:简介join()函数和detach()函数与主线程关联
铺垫:简介join()函数和detach()函数与主线程关联
C++ 使用thread对象时,join()函数会阻塞主线程,detach()函数不会阻塞主线程同时会脱离主线运行,简单验证如下
代码:
// Example program
#include <iostream>
#include <string>
#include <thread>
void FuncPintLog(int num =1)
{
std::cout << "thread: " << num << " has started!!\n";
}
int main()
{
std::cout << "main begin\n";
std::thread t1(FuncPintLog, 1);
std::cout << " join begin !\n";
t1.join();
std::cout << " join end !\n";
thread t2(FuncPintLog, 2);
std::cout << " detach begin !\n";
t2.detach();
std::cout << " detach end !\n";
std::cout << "main end\n";
return 0;
}
运行结果
main begin
join begin !
thread: 1 has started!!
join end !
detach begin !
detach end !
main end
thread: 2 has started!!
同时需要注意线程thread对象如果不使用join()函数或detach()函数,会报错抛出异常。
1.detach()函数存在隐患
在实际代码编写过程中,并不推荐使用detach()函数,因为使用detach()函数后子线程生存周期不可控制,同时无法使用主线程中定义的变量(主线程结束后会释放主线程中定义的变量值,而此时子线程继续运行,刚好使用该变量),例子如下
// Example program
#include <iostream>
#include <string>
#include <thread>
class TestThread {
public:
TestThread() {};
~TestThread() {};
void FuncPintLog(int num)
{
std::cout << "thread num: " << num << " has started!!\n";
}
void Init()
{
t1 = std::thread(&TestThread::FuncPintLog, this, a);
}
void Start() {
std::cout << " detach begin !\n";
t1.detach();
std::cout << " detach end !\n";
}
void SetPara(int num)
{
a= num;
}
private:
std::thread t1;
int a;
};
int main()
{
std::cout << "main begin\n";
TestThread* testThread = new TestThread();
testThread->SetPara(10);
testThread->Init();
testThread->Start();
delete testThread;
std::cout << "main end\n";
return 0;
}
执行结果为
main begin
detach begin !
detach end !
main end
thread num:
可以看出并没有输出我们预期的" thread num: 10 ", 因为主线程结束后,将a = 10释放掉,所以无法输出“10”;如果将“t1.detach()” 改为"t1.join()",则可以输出thread num: 10. 输出结果为
main begin
detach begin !
thread num: 10 has started!!
detach end !
main end
综上所述,还是建议使用thread对象的join函数。
2.join()函数调用时机-阻塞调用
通常我们希望在子线程中进行耗时操作,同时不阻碍主线程,那么在什么时机调用join()函数呢,先看下面这段代码
// Example program
#include <iostream>
#include <string>
#include <thread>
#include <unistd.h>
void NotWantToBeBlock()
{
std::cout << "This func need to run before 'FuncPintLog finish' \n ";
}
class TestThread {
public:
TestThread() {};
~TestThread() {};
void FuncPintLog(int num)
{
std::cout << "thread num: " << num << " has started!!\n";
sleep(3);
std::cout << "FuncPintLog finish!\n ";
}
void Init()
{
t1 = std::thread(&TestThread::FuncPintLog, this, a);
}
void Start() {
std::cout << " join begin !\n";
t1.join();
std::cout << " join end !\n";
}
void SetPara(int num)
{
a= num;
}
private:
std::thread t1;
int a;
};
int main()
{
std::cout << "main begin\n";
TestThread testThread;
testThread.SetPara(10);
testThread.Init();
testThread.Start();
NotWantToBeBlock();
std::cout << "main end\n";
return 0;
}
输出结果
main begin
join begin !
thread num: 10 has started!!
FuncPintLog finish!
join end !
This func need to run before 'FuncPintLog finish'
main end
可以看出由于主线程被阻塞“NotWantToBeBlock()”函数的输出日志出现在“FuncPintLog finish!”后面,并不是我们预期的结果,我们希望子线程"t1"不会阻塞主线程同时与主线程并行,其中线程函数FuncPintLog(int num)增加延时"3s",可以保证线程函数的执行时长。
3.join()函数调用时机-析构函数中调用
下面调整thread对象"t1"的join函数使用时机,将其放在所在对象的析构函数中,同时不会调用Start()函数,代码如下
// Example program
#include <iostream>
#include <string>
#include <thread>
#include <unistd.h>
void NotWantToBeBlock()
{
std::cout << "This func need to run before 'FuncPintLog finish' \n ";
}
class TestThread {
public:
TestThread() {};
~TestThread()
{
std::cout << " ~TestThread join begin !\n";
t1.join();
std::cout << " ~TestThread join end !\n";
};
void FuncPintLog(int num)
{
std::cout << "thread num: " << num << " has started!!\n";
sleep(3);
std::cout << "FuncPintLog finish!\n ";
}
void Init()
{
t1 = std::thread(&TestThread::FuncPintLog, this, a);
}
void Start() {
std::cout << " join begin !\n";
t1.join();
std::cout << " join end !\n";
}
void SetPara(int num)
{
a= num;
}
private:
std::thread t1;
int a;
};
int main()
{
std::cout << "main begin\n";
TestThread testThread;
testThread.SetPara(10);
testThread.Init();
// testThread.Start();
NotWantToBeBlock();
std::cout << "main end\n";
return 0;
}
运行结果
main begin
This func need to run before 'FuncPintLog finish'
main end
~TestThread join begin !
thread num: 10 has started!!
FuncPintLog finish!
~TestThread join end !
代码中创建对象TestThread没有采用new的方式创建,使其作为临时变量创建在栈上,在主线程的main()函数结束后将会被释放,调用析构函数~TestThread(),此时就会调用线程的join()函数阻塞主线程结束,同时又不会阻塞线程中其他函数NotWantToBeBlock,所以使用join函数的一个好时机就是创建全局子线程对象,并且在当前所在对象的析构函数中调用join函数。
另外在使用g++编译c++代码时,若调用<thread>,需要在编译命令后面加入“-lpthread”,否则会出现undefined reference to `pthread_create'报错
希望这篇文章能够对开发C++多线程的你有用,欢迎大家一起讨论,献丑了~~~