C++ thread’s join() function uses a small coup

Directory

Foreshadowing: Introduction to the join() function and the detach() function are associated with the main thread

1. There are hidden dangers in the detach() function

2. Join() function call timing – blocking call

3. Join() function call timing – call in the destructor


Foreshadowing: Introduction to the join() function and The detach() function is associated with the main thread

When C++ uses the thread object, the join() function will block the main thread, and the detach() function will not block the main thread and run away from the main thread. Simple verification is as follows

code:

// Example program
#include <iostream>
#include <string>
#include <thread>

void FuncPintLog(int num =1)
{
    std::cout << "thread: " << num << " has started!!\
";
}


int main()
{
  std::cout << "main begin\
";

  std::thread t1(FuncPintLog, 1);
  std::cout << " join begin !\
";
  t1. join();
  std::cout << " join end !\
";

  thread t2(FuncPintLog, 2);
  std::cout << "detach begin !\
";
  t2. detach();
  std::cout << "detach end !\
";

  std::cout << "main end\
";
  return 0;
}

operation result

main begin
 join begin!
thread: 1 has started!!
 join end!
 detach begin!
 detach end!
main end
thread: 2 has started!!

At the same time, it should be noted that if the thread object does not use the join() function or the detach() function, an error will be reported and an exception will be thrown.

1. There are hidden dangers in the detach() function

In the actual code writing process, it is not recommended to use the detach() function, because the life cycle of the sub-thread is uncontrollable after using the detach() function, and the variables defined in the main thread cannot be used (the variables defined in the main thread will be released after the main thread ends. variable value, and the child thread continues to run at this time, just using this variable), the example is as follows

// Example program
#include <iostream>
#include <string>
#include <thread>


class TestThread {
public:
    TestThread() {};
    ~TestThread() {};

    void FuncPintLog(int num)
    {
        std::cout << "thread num: " << num << " has started!!\
";
    }
    
    void Init()
    {
        t1 = std::thread( &TestThread::FuncPintLog, this, a);
    }

    void Start() {
        std::cout << "detach begin !\
";
        t1. detach();
        std::cout << "detach end !\
";
    }
    
    void SetPara(int num)
    {
        a = num;
    }

private:
    std::thread t1;
    int a;
};

int main()
{
  std::cout << "main begin\
";
  TestThread* testThread = new TestThread();
  testThread->SetPara(10);
  testThread->Init();
  testThread->Start();
  delete testThread;
  std::cout << "main end\
";
  return 0;
}

The execution result is

main begin
 detach begin!
 detach end!
main end
thread num: 

It can be seen that the expected “thread num: 10” is not output, because after the main thread ends, a = 10 is released, so “10” cannot be output; if “t1.detach()” is changed to “t1. join()”, you can output thread num: 10. The output result is

main begin
 detach begin!
thread num: 10 has started!!
 detach end!
main end

In summary, it is recommended to use the join function of the thread object.

2.join() function calling timing – blocking call

Usually we want to perform time-consuming operations in sub-threads without hindering the main thread, so when to call the join() function, first look at the following code

// Example program
#include <iostream>
#include <string>
#include <thread>
#include <unistd.h>

void NotWantToBeBlock()
{
    std::cout << "This func need to run before 'FuncPintLog finish' \
 ";
}

class TestThread {
public:
    TestThread() {};
    ~TestThread() {};

    void FuncPintLog(int num)
    {
        std::cout << "thread num: " << num << " has started!!\
";
        sleep(3);
        std::cout << "FuncPintLog finish!\
 ";
    }
    
    void Init()
    {
        t1 = std::thread( &TestThread::FuncPintLog, this, a);
    }

    void Start() {
        std::cout << " join begin !\
";
        t1. join();
        std::cout << " join end !\
";
    }
    
    void SetPara(int num)
    {
        a = num;
    }

private:
    std::thread t1;
    int a;
};

int main()
{
  std::cout << "main begin\
";
  TestThread testThread;
  testThread. SetPara(10);
  testThread. Init();
  testThread. Start();
  NotWantToBeBlock();
  std::cout << "main end\
";
  return 0;
}

output result

main begin
 join begin!
thread num: 10 has started!!
FuncPintLog finish!
  join end!
This func need to run before 'FuncPintLog finish'
 main end

It can be seen that because the main thread is blocked, the output log of the “NotWantToBeBlock()” function appears after “FuncPintLog finish!”, which is not the result we expected. We hope that the child thread “t1” will not block the main thread and run parallel with the main thread at the same time , where the thread function FuncPintLog(int num) increases the delay “3s”, which can guarantee the execution time of the thread function.

3.join() function call timing – call in the destructor

Next, adjust the use timing of the join function of the thread object “t1”, put it in the destructor of the object where it is located, and will not call the Start() function at the same time, the code is as follows

// Example program
#include <iostream>
#include <string>
#include <thread>
#include <unistd.h>

void NotWantToBeBlock()
{
    std::cout << "This func need to run before 'FuncPintLog finish' \
 ";
}

class TestThread {
public:
    TestThread() {};
    ~TestThread()
    {
        std::cout << " ~TestThread join begin !\
";
        t1. join();
        std::cout << " ~TestThread join end !\
";
    };

    void FuncPintLog(int num)
    {
        std::cout << "thread num: " << num << " has started!!\
";
        sleep(3);
        std::cout << "FuncPintLog finish!\
 ";
    }
    
    void Init()
    {
        t1 = std::thread( &TestThread::FuncPintLog, this, a);
    }

    void Start() {
        std::cout << " join begin !\
";
        t1. join();
        std::cout << " join end !\
";
    }
    
    void SetPara(int num)
    {
        a = num;
    }

private:
    std::thread t1;
    int a;
};

int main()
{
  std::cout << "main begin\
";
  TestThread testThread;
  testThread. SetPara(10);
  testThread. Init();
// testThread. Start();
  NotWantToBeBlock();
  std::cout << "main end\
";
  return 0;
}

operation result

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 !

The object TestThread created in the code is not created in the new way, so that it is created on the stack as a temporary variable. It will be released after the main() function of the main thread ends, and the destructor ~TestThread() will be called. The join() function of the calling thread blocks the end of the main thread, and at the same time does not block other functions in the thread NotWantToBeBlock, so a good time to use the join function is to create a global sub-thread object, and in the destructor of the current object Call the join function.

In addition, when using g++ to compile c++ code, if you call , you need to add “-lpthread” after the compilation command, otherwise an undefined reference to `pthread_create’ error will appear

I hope this article can be useful to you who develop C ++ multi-threading. Welcome everyone to discuss and show your ugliness~~~