• join()函数和detach()函数有什么作用?

  请看下面的例子,我们首先创建两个线程并让其自行执行;

#include<thread>
#include<iostream>
using namespace std;

void func1(){
    for(int i = 0; i < 10; i++) cout << i;
}

void func2(){
    for(int i = 0; i < 10; i++) cout << char('a'+i);
}

int main(){
    thread t1(func1);
    thread t2(func2);return 0;
}
// output is empty

   如果直接在主线程中创建子线程并执行,则主线程往往先于子线程执行完毕(先获得CPU的控制权),导致整个进程的异常。因此,我们需要使用join()函数将子线程阻塞。

  在哪个线程函数内创建线程,则两个线程就为父子关系;

  在父线程函数内使用子线程的join函数,则将父线程阻塞,等待被join的子线程执行完毕才能继续执行完父线程。

void func2(){
    for(int i = 0; i < 10; i++) cout << char('a'+i);
}
void func1(){
    thread t2(func2);
    t2.join();
    for(int i = 0; i < 10; i++) cout << i;
}

int main(){
    thread t1(func1);
    t1.join();
    return 0;
}

  总之,join用于阻塞当前线程等待thread执行完执行体。一般用于多线程之间进行同步。对于不具备分离属性的线程必须要join,否则会导致terminal。

   上面我们了解到,线程间是并行运行的,但是如果主线程先结束会导致子线程也结束,导致进程异常;利用join()函数我们可以使线程间串行运行。

  如果需要线程间并行运行,且子线程与父线程分离,当父线程执行结束后子线程不会随之一起结束,而是自行在后台运行,这是detach()函数的作用。

#include <iostream>       // std::cout
#include <thread>         // std::thread, std::this_thread::sleep_for
#include <chrono>         // std::chrono::seconds
 
void pause_thread(int n) 
{
  std::this_thread::sleep_for (std::chrono::seconds(n));
  std::cout << "pause of " << n << " seconds ended\n";
}
 
int main() 
{
  std::cout << "Spawning and detaching 3 threads...\n";
  std::thread (pause_thread,3).detach();
  std::thread (pause_thread,1).detach();
  std::thread (pause_thread,2).detach();
  std::cout << "Done spawning threads.\n";

  std::cout << "(the main thread will now pause for 5 seconds)\n";
  // give the detached threads time to finish (but not guaranteed!):
  pause_thread(3);
  return 0;
}

  上面的代码中,3个子线程将会脱离主线程自行运行。但是!所谓的后台运行显然让控制台中的输出结果无法被观测,因此我们利用下面的代码证明detach的线程仍在后台运行。

#include<iostream>
#include<thread>
#include<fstream>
#define pi 3.1415926
using namespace std;
double compute(int i){
    double temp  = 1;
    for(int j = 0; j < 100000000; j++){
        temp *= pi;
        temp /= i;
    }
    return temp;
}

void write_file(){
    fstream file("./log.txt");
    for(int i = 1; i < 100; i++){
        file << i << ' ' << compute(i) << endl;
    }
    return ;
}

int main(){
    // write_file(file);
    thread (write_file).detach();
    for(int i = 1; i < 20; i++){
        // 作耗时的浮点数运算
        cout << i << ' ' << compute(i) << endl;
    }
    return 0;
}

  结果被打脸了,主线程结束后子线程也结束了,说好了在后台运行呢?

  上面的代码在主线程结束后,子线程也随之结束。因此这里的“后台运行”还需要进一步理解。

  请看下面的代码:

#include<iostream>
#include<thread>
#include<fstream>
#define pi 3.1415926
using namespace std;
double compute(int i){
    double temp  = 1;
    for(int j = 0; j < 100000000; j++){
        temp *= pi;
        temp /= i;
    }
    return temp;
}

void print(){
    for(int i = 1; i < 100; i++){
        cout << i << ' ' << compute(i) << endl;
    }
    return ;
}

void write_file(){
    fstream file("./log.txt");
    thread t(print);  // 注意这里没加detach,加了detach()之后子线程不会随父线程关闭
    for(int i = 1; i < 20; i++){
        file << i << ' ' << compute(i) << endl;
    }
    return ;
}

int main(){
    thread (write_file).detach();
    system("pause");
    return 0;
}

  直接创建线程并运行,结果在父线程write_file结束后,print子线程也随之结束,报错

  “terminate called without an active exception”

  detach()函数可以使一个线程变为non-joinable状态,从而无法对父线程进行阻塞。

  当父线程不是主线程时,对子线程加detach()可使其在后台运行。