C++并行开发3-线程传参详解,detach()大坑,成员函数做线程函数

FengLY Lv3

thread传入类对象

#include <iostream>
#include <string>
#include <thread>


using namespace std;

void myprint(const int i, string mybuf) {
    cout << i << endl;
    cout << mybuf << endl;   //直接传string也是可以的,因为会拷贝构造
}


//void myprint(const int& i, //通过shift + F9 调试发现,这里传的引用但是也会拷贝一下,是值传递,这样即使main退出,也是安全的
//  char* mybuf) {    //但是堆上的数据是不安全的,是传引用,指针和引用都是由问题的
//  cout << i << endl;
//  cout << mybuf << endl;   //直接传string也是可以的,因为会拷贝构造
//}

int main()
{
    //传递临时对象作为线程参数
    int mval = 1;
    int &myvaly = mval;

    char mybuf[] = "this is a test ";
    //thread myobj(myprint, mval, mybuf); 
    //问题 mybuf是什么时候转化为string 的, 有可能出现mybuf都被回收了,系统才用mybuf转string的可能性

    thread myobj(myprint, mval, string(mybuf)); //这里直接将mybuf 转化为 string
    myobj.join();

    cout << "主线程运行完毕" << endl;
    return 0;
}

上述代码总结,栈上的数据在创建线程的时候会拷贝,但是堆上的数据在创建线程的时候不会拷贝

class A {
public:
    int m_i;
    A(int i) : m_i(i) {   //这种构造函数也可以被称为强制类型转化函数
        cout << "A 的构造函数" << endl;
    }

    A(const A& other) :m_i(other.m_i) {
        cout << "A 的拷贝构造函数" << endl;
    }

    ~A() {
        cout << "A的析构函数" << endl; 
    }
};


void myprint(const int i, const A& mybuf) {
    cout << i << endl;
    cout << &mybuf << endl;
}


int main()
{
    //传递临时对象作为线程参数
    int mval = 1;
    int &myvaly = mval;
    int second = 12;
    thread myobj(myprint, mval, mval); //这里直接将mybuf 转化为 string
    myobj.detach();
    cout << "主线程运行完毕  111" << endl;
    return 0;
}

这种情况下,有可能出现mval还没有强制转换,就已经线程结束的情况,从下图可以看到,A有可能没有构造完毕,主线程就推出了

int main()
{
    //传递临时对象作为线程参数
    int mval = 1;
    int &myvaly = mval;
    int second = 12;
    //thread myobj(myprint, mval, mval); //这里直接将mybuf 转化为 string
    thread myobj(myprint, mval,A(mval)); //在创建线程的同时构造临时对象的方法是可行的
    myobj.detach();
    cout << "主线程运行完毕  111" << endl;
    return 0;
}

如果先临时构造一下,就可以正常构建

小结

  • 1. 若传递的是int这种简单的类型参数,建议直接值传递,不要传引用,以免节外生枝
    
  • 2. 如果传递类对象,避免隐式类型转换, 全部都在传递参数的时候,构建一个临时变量,用引用来接,否则系统就会构造3次
    

线程id

线程id 可以通过std::this_thread::get_id()

传递类对象,智能指针作为线程参数

#include <iostream>
#include <string>
#include <thread>

using namespace std;


class A {
public:
    mutable int m_i;
    A(int i) : m_i(i) {   //这种构造函数也可以被称为强制类型转化函数
        cout << "A 的构造函数" << "Id : " << std::this_thread::get_id() << endl;
    }

    A(const A& other) :m_i(other.m_i) {
        cout << "A 的拷贝构造函数" << std::this_thread::get_id() << endl;
    }

    ~A() {
        cout << "A的析构函数" << std::this_thread::get_id() << endl;
    }
};


void myprint(const int i, const A& mybuf) {
    cout << i << endl;
    mybuf.m_i = 199;  //已经拷贝构造了,这里修改但是没有用
}


int main()
{
    //传递临时对象作为线程参数
    int mval = 1;
    int &myvaly = mval;
    int second = 12;

    A a(mval);

    //thread myobj(myprint, mval, a);  //这里传入进去并不会修改A里面的值

    thread myobj(myprint, mval, std::ref(a));  //这里传入进去并不会修改A里面的值
    // 不加std::ref myprin里面就必须要加上const 并且引用,否则会报错

    myobj.detach();
    cout << "A  " << a.m_i << endl;
    cout << "主线程Id: " << std::this_thread::get_id() << endl; 
    cout << "主线程运行完毕  111" << endl;
    return 0;
}

可以使用std::ref()改变类中的成员数据。

void myprint(unique_ptr<int> i) {
    cout << "i = " << i << endl;
}

int main()
{
    unique_ptr<int> myp(new int(100));
    //shared_ptr<int> myp(new int(100));
    thread mtobj(myprint, myp);  //报错  unique_ptr不支持拷贝构造, shared_ptr就可以

    //thread mtobj(myprint, std::move(myp));

    mtobj.join();
    return 0;
}

unique_ptr不支持拷贝构造, shared_ptr就可以, 如果一定要用move也可以使用std::move

#include <iostream>
#include <string>
#include <thread>

using namespace std;


class A {
public:
    mutable int m_i;
    A(int i) : m_i(i) {   //这种构造函数也可以被称为强制类型转化函数
        cout << "A 的构造函数" << "Id : " << std::this_thread::get_id() << endl;
    }

    A(const A& other) :m_i(other.m_i) {
        cout << "A 的拷贝构造函数" << std::this_thread::get_id() << endl;
    }

    void thread_word(int num) {
        cout << "num is " << num << endl;
    }

    void operator()(int num) {
        cout << "operator " << num << endl;
    }

    ~A() {
        cout << "A的析构函数" << std::this_thread::get_id() << endl;
    }
};


//void myprint(const int i, const A& mybuf) {
//  cout << i << endl;
//  mybuf.m_i = 199;  //已经拷贝构造了,这里修改但是没有用
//}


void myprint(unique_ptr<int> i) {
    cout << "i = " << i << endl;
}

int main()
{
    A myobj(1);
    std::thread mytobj(&A::thread_word, myobj, 500);
    //std::thread mytobj(myobj, 500);

    mytobj.join();  //这里只能用到join 因为thread中需要传入myobj
    return 0;
}

使用类成员函数作为线程对象 总结:使用std::ref不会再使用拷贝构造函数,std::move是真的左移

  • Title: C++并行开发3-线程传参详解,detach()大坑,成员函数做线程函数
  • Author: FengLY
  • Created at : 2023-06-18 22:40:09
  • Updated at : 2023-06-18 22:55:37
  • Link: https://zhouaq.com/2023/06/18/C++并行开发3-线程传参详解,detach()大坑,成员函数做线程函数/
  • License: This work is licensed under CC BY-NC-SA 4.0.
Comments
On this page
C++并行开发3-线程传参详解,detach()大坑,成员函数做线程函数