三、线程传参

一、thread线程对象的参数详解

1、线程函数参数是值传递

void mythread(int i){//如果不对i修改,可以改成const int i
    cout<<&i<<endl;
}
int main(){
    int a=1;
    thread thread1(mythread,a);//第一个参数是线程函数名,后面的是线程函数的参数,值传递
    thread1.join();//使用detach不会报错
    cout<<&a<<endl;
}

将a传入后,线程入口函数采用值传递方式先将a拷贝得到副本(即i)。所以此时的 i 和 a 的地址不同。

值传递时,使用detach()也不会出问题。

2、线程函数引用传递参数

当线程入口函数的参数列表中有引用,这个时候很多问题。

(1)构造thread对象时直接使用主线程中的对象

定义线程函数时引用的参数必须加const!因此只能只读。

int mythread(const int& i){//不加const会报错!
    cout<<"thread:"<<&i<<endl;
    return 0;
}
int main(){
    int a=1;
    thread thread1(mythread,a);
    thread1.join();//使用detach也不会出问题
    cout<<&a<<endl;
    return 0;
}

此时i的地址和a的地址并不相同。虽然线程函数参数是引用,但构建thread1时还是拷贝出了一个a副本,线程函数引用的是副本。

(2)真正使用引用的方法:ref函数

这时在定义线程函数时,参数前的const可以去掉。

int mythread(int& i){//const去掉
    i+=1;
    cout<<"thread:"<<&i<<endl;
    cout<<"i= "<<i<<endl;
    return 0;
}
int main(){
    int a=1;
    thread thread1(mythread,ref(a));//真正意义上的引用
    thread1.join();//detach()也不会出错
    cout<<"main:"<<a<<endl;
    cout<<"main地址:"<<&a<<endl;
    return 0;
}

3、传递字符串

int mythread(char* buf){
//    cout<<buf<<endl;
//    cout<<&buf<<endl;
    cout<<&buf<<endl;
    cout<<"thread end"<<endl;
    return 0;
}
int main(){
    char buf[]="i love china";
    thread thread1(mythread,buf);//数组是指针传递,传的是第0个元素的地址。
    thread1.join();
    cout<<"main"<<endl;
    return 0;
}

字符数组是数组,传递属于指针传递,数组传递的是第0个元素的地址,万一主线程执行完,buf回收,而子线程用到了buf[888]类似于这样的地址,那么就相当于指向了一个回收的内存地址,野指针现象发生。!!

如果在定义线程入口函数时,隐式转换成string。存在的问题是,万一主线程执行完了,子线程还没转换完,还是会出现野指针的现象,程序还是会报错。

因此对于字符串、类对象可以在构造thread对象时,构建临时对象即可:

class A{
public:
    int& m_i;
    A(int& i):m_i(i){}
    A(const A& a):m_i(a.m_i){cout<<"拷贝构造函数执行"<<endl;}
    ~A(){cout<<"析构函数执行"<<endl;}
}
void mythread(string& buf,const A& a){//引用传递
    cout<<"thread"<<endl;
}

int main(){
    int a=9;
    char buf[]=="ilovechina"
    thread thread1(mythread,string(buf),A(a));//构造两个临时对象
    thread1.detach();//join、detach都没问题
    cout<<"main end"<<endl;
    return 0;
}

4、智能指针

#include <iostream>
#include <thread> //线程
using namespace std;

void prt(unique_ptr<int> p){
    cout<<"子线程开始了"<<endl;
}
int main(){
    unique_ptr<int> myptr(new int(100));
    std::thread myobj(prt,std::move(myptr));//move将myptr给了临时的指针变量,之后myptr就没了,
    //如果用detach,万一myprt指向的内存回收了,那么子线程的这个指针就指向了一个被回收的内存地址,就会出错,所以这个时候不能用detach
    //只能用join
    myobj.join();
    cout<<"main end"<<endl;
    return 0;
}

5、成员函数指针做线程参数

#include <iostream>
#include <thread> //线程
using namespace std;

class A{
public:
    int m_i;
    A(int a):m_i(a){cout << "A(int)构造函数执行了"<<"类对象的地址是:"<<this<<endl;}
    A(const A &a):m_i(a.m_i){cout<<"A(const)的拷贝构造函数执行了"<<endl;}
    ~A(){cout<<"A的析构函数执行"<<endl;}

    void thread_work(int num){
        //线程执行入口
        cout << "子线程开始"<<endl;
    }
};

int main(){
    A my(10);
    std::thread myobj(&A::thread_work,my,15);//注意成员函数参数写在后面,这时候的my已经是一个复制后的临时变量
    //thread myobj(&A::thread_work,&my,15)==(&A::thread_work,std::ref(my),15);这个时候就么有用拷贝构造函数,没有复制,用的就是my自己,此时不能用detach
    myobj.join();
    cout<<"main end"<<endl;
    return 0;
}

 

二、线程id

线程id可以用c++获取到:std::this_thread::get_id()来获取。

//在各自线程函数中:
cout << std::this_thread::get_id()<<endl;

坑以及注意事项

传递指针时是浅拷贝,即使线程函数定义时是引用,引用的只是副本而已。

主线程与子线程指针指向的同一内存,这样的情况很危险,多个指针指向同一个内存地址,子线程对这个内存对象的更改会影响到主线程中其他指针的使用。

在 C++中,数组永远不会按值传递,它是传递第一个元素,准确地说是第 0个 的指针。

c++数组没有引用的规则,定义函数时不能用引用传递数组。string是类,string类对象可以被引用。

 

posted @ 2019-07-22 19:51  Austin_anheqiao  阅读(738)  评论(0编辑  收藏  举报