C++ thread的join()函数使用小妙招

目录

铺垫:简介join()函数和detach()函数与主线程关联

1.detach()函数存在隐患

2.join()函数调用时机-阻塞调用

3.join()函数调用时机-析构函数中调用


铺垫:简介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++多线程的你有用,欢迎大家一起讨论,献丑了~~~

  • 5
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值