c++11 thread多线程 thread()、join()、detach()

  • Post author:
  • Post category:其他



目录


0.linux下thread编译命令


1.thread构造函数


1).以函数为参数


2).以可调对象为参数


3).以lambda表达式为参数


2.join()函数


3.detach()函数


4.joinable()函数


0.linux下thread编译命令

g++ 源文件名.cpp -std=c++11 -lpthread
或
g++ 源文件名.cpp -std=c++11 -lpthread -o 目标文件名

1.thread构造函数

//无参构造函数,创建空线程
thread() noexcept;

//初始化构造函数,执行fn函数,fn函数的参数由Args给出
template <class Fn, class... Args>
explicit thread(Fn&& fn, Args&&... args);

//拷贝构造函数,被禁用
thread(const thread&) = delete;

//move构造函数 不会(划掉)
thread(thread&& x) noexcept;

一般来说,最常用的就是初始化构造函数,因此就初始化构造函数的三种方式进行说明

1).以函数为参数

#include<bits/stdc++.h>
using namespace std;

void print(){
    cout<<"子线程"<<endl;
    cout<<"子线程"<<endl;
    cout<<"子线程"<<endl;
}

int main(){
    thread my(print);
    cout<<"主线程"<<endl;
    my.join();
  
    return 0;
}


运行结果:

可以发现,如果按照平时的思维,应该是先调用函数,函数结束之后再执行主线程。但是根据输出很明显不是这样。这就是线程的特别之处了。那是创建了子线程之后,就相当于子线程与主线程就分道扬镳了,各走各的路,因此执行的顺序是不可预估的。

2).以可调对象为参数

#include<bits/stdc++.h>
using namespace std;

class A{
public:
    bool operator()(int i){
        cout<<i<<endl;
    }
};

int main(){
    A a;
    int i = 1;
    thread my(a,i);
    cout<<"主线程"<<endl;
    my.join();
  
    return 0;
}

使用可调对象时,必须重载”()”。

3).以lambda表达式为参数

#include<bits/stdc++.h>
using namespace std;

int main(){
    auto a = [](){cout<<"lambda"<<endl;};
    thread my(a);
    cout<<"主线程"<<endl;
    my.join();
  
    return 0;
}

2.join()函数

既然子线程被调用后,与主线程处于不同的道路了,那么考虑下面这个求10!的例子。

#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
LL ans = 1;
void cal(int n){
    for(int i = 1;i <= n;++i) ans *= i;
}

int main(){
    int n = 10;
    thread my(cal,10);
    cout<<ans<<endl;
  
    return 0;
}

结果:

1
terminate called without an active exception
已放弃 (核心已转储)

很明显答案是错的,那么是为什么呢。我们说过子线程与主线程是两条不同的路,而且两者可以看做是同时进行的,也就是说在你还没有完成子进程的计算的时候,主进程已经进行到了cout<<ans<<endl;这一步了,而此时的ans还没有计算完成,所以出现了错误。那么怎么解决这个问题呢?这就要用到join()函数了。

join()函数实际上起到了一个阻塞的作用。就是说阻塞当前线程,直到调用join函数的线程结束之后,当前线程才能继续往下进行。

#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
LL ans = 1;
void cal(int n){
    for(int i = 1;i <= n;++i) ans *= i;
}

int main(){
    int n = 10;
    thread my(cal,10);
    my.join();
    cout<<ans<<endl;
    
    return 0;
}

运行结果:

3628800

3.detach()函数

与join()函数的暂时分工合作最后汇合不同,detach()函数是彻底的分道扬镳。也就是说调用detach()之后,主线程不再管子线程,就算主线程结束也没关系,子线程仍然在执行,只不过这时候子线程转到了后台运行,并且由c++运行时库管理。因为调用detach()之后子线程就失控了,所以使用detach()之后有时候会出现一些不可预计的错误,之后再来讨论。

#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
LL ans = 1;
void cal(int n){
    for(int i = 1;i <= n;++i) ans *= i;
}

int main(){
    int n = 10;
    thread my(cal,10);
    my.detach();
    cout<<ans<<endl;
    
    return 0;
}

上述程序的结果不可预计。

4.joinable()函数

一个线程只能一次join()或者一次detach()。joinable的返回值为true或者false,当当前线程返回值为true时,可以执行join()或者detach(),当返回值为false时,不能执行join()或者detach().

#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
LL ans = 1;
void cal(int n){
    for(int i = 1;i <= n;++i) ans *= i;
}

int main(){
    int n = 10;
    thread my(cal,10);
    if(my.joinable()) my.join();
    cout<<ans<<endl;
    
    return 0;
}



版权声明:本文为qq_41167110原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。