多线程编程2

传递临时对象作为线程参数

要避免的陷阱1

  • 线程入口的为函数,入口函数的形参为引用、指针
#include <iostream>
#include <thread>

using namespace std;


//定义函数,可调用的对象。作为线程参数的入口
void myPrint(const int &i, char *pmybuf){
    cout << "形参 const int &i = " << i << endl;		//&i = 0x1031e90 , *&i = 1
    cout << "形参 char *pmybuf = " << pmybuf << endl;	 //&pmybuf = 0x284fcf8, *&pmybuf = 0x62fdf0,pmybuf作为一个指针变量,存放了实参的地址
}		
}

int main(){

    int mvar = 1;           // &mvar = 0x62fe04, *&mvar = 1
    int &mvary = mvar;      //定义mvar的引用,是变量mvar的别名  &mvary = 0x62fe04, *&mvary = 1
    char mybuf[] = "this is a test!";   //&mybuf = 0x62fdf0, 
    thread myThread(myPrint, mvar, mybuf);  //定义myThread线程,函数myPrint作为线程参数的入口传入线程。mvar、mybuf作为函数myPrint的参数传入函数
    myThread.join();    //主线程等待子线程结束
    cout << "I Love China!" << endl;
    return 0;

}
  • 使用join的运行结果
    在这里插入图片描述
  • 使用detach的运行结果
    在这里插入图片描述

结论1

  • 引用形参i的地址和主线程中mvar的地址不一样,所以即使主线程提前结束释放了mvar的内存空间,子线程中的i仍然可以正常使用,可以使用detach
  • 指针形参pmybuf的地址和主线程中mybuf的地址相同,所以如果主线程提前结束,释放了mybuf的地址空间,子线程中的pmybuf便不能正常使用,不可使用detach

要避免的陷阱2

  • 在函数的形参传递时使用隐式类型转换,将char型字符数组转换成string
#include <iostream>
#include <thread>

using namespace std;

void myPrint2(const int &i, const string &pmybuf){
    cout << "myPrint2 形参 const int &i = " << i << endl;            //&i = 0xe91e90, *&i = 1
    cout << "myPrint2 形参 string &pmybuf = " << pmybuf << endl;     //&pmybuf = 0x27afce0
}

int main(){
    int mvar = 1;           // &mvar = 0x62fe04, *&mvar = 1
    int &mvary = mvar;      //定义mvar的引用,是变量mvar的别名  &mvary = 0x62fe04, *&mvary = 1
    char mybuf[] = "this is a test!";   //&mybuf = 0x62fdf0, 
    thread myThread2(myPrint2, mvar, mybuf);
    myThread2.join();
   // myThread2.detach();
    cout << "I Love China!" << endl;
    return 0;
}
  • 使用join运行结果
    在这里插入图片描述
  • 使用detach运行结果
    在这里插入图片描述
  • 定义一个类A来类比string类,线程函数入口进行隐式类型转换
class A{
    public:
    int m_i;     //&m_i = 0x62fe0c, *&m_i = 0
    A(int a):m_i(a) { 
        cout << "[A::A(int a)构造函数执行]" << endl; 
        cout << "类A的构造函数的线程id : " << std::this_thread::get_id() << endl;
        } //类型转换构造函数,将整型a转换为类A的对象m_i; 将传入的i的值赋给m_i。  &i = 0x62fde8, *&i = 10
    A(const A &a):m_i(a.m_i) {cout << "[A::A(const A)构造函数执行]" << endl;}
    ~A() {cout << "[A::~A()析构函数执行]" << endl;}
};

void myPrint3(const int &i, const A& mysecond){     //函数的形参为A类型的引用pmybuf
   cout << "myPrint3 形参 const int &i = " << i << endl;
   cout << "myPrint3 形参 const A& pmybuf = " << mysecond.m_i << endl;  
   cout << "线程入口myPrint3的线程id为: " << std::this_thread::get_id() << endl;
}
int main(){
   int mvar = 1;
   int mysecond = 12;
   thread myThread3(myPrint3, mvar, mysecond);   //第一个参数为函数,作为线程的入口;第二个参数为类A的对象,作为函数myPrint3的实参
   myThread3.join();  
   cout << "主线程的id是: " << std::this_thread::get_id() << endl;
    cout << "I Love China!" << endl;
    return 0;
}

在这里插入图片描述

  • 类A的构造函数(隐式类型转换是在子线程中完成的)
  • 在线程传参时,在线程参数传入时进行类型转换
class A{
    public:
    int m_i;     //&m_i = 0x62fe0c, *&m_i = 0
    A(int a):m_i(a) { 
        cout << "[A::A(int a)构造函数执行]" << endl; 
        cout << "类A的构造函数的线程id : " << std::this_thread::get_id() << endl;
        } //类型转换构造函数,将整型a转换为类A的对象m_i; 将传入的i的值赋给m_i。  &i = 0x62fde8, *&i = 10
    A(const A &a):m_i(a.m_i) {
        cout << "[A::A(const A)拷贝构造函数执行]" << endl;
        cout << "类A的拷贝构造函数的线程id : " << std::this_thread::get_id() << endl;
    }
    ~A() {
        cout << "[A::~A()析构函数执行]" << endl;
        cout << "类A的析构函数的线程id : " << std::this_thread::get_id() << endl;
        }
};
void myPrint3(const int &i, const A &mysecond){     //函数的形参为A类型的引用pmybuf
   cout << "myPrint3 形参 const int &i = " << i << endl;
   cout << "myPrint3 形参 const A& pmybuf = " << mysecond.m_i << endl;
  // cout << "myPrint3 形参 const A& pmybuf 的地址 &pmybuf 为 = " << &mysecond << endl;    
   cout << "线程入口myPrint3的线程id为: " << std::this_thread::get_id() << endl; 
}
int main(){
   int mvar = 1;
   int mysecond = 12;
   thread myThread3(myPrint3, mvar,A(mysecond));   //第一个参数为函数,作为线程的入口;第二个参数为类A的对象,作为函数myPrint3的实参,在向线程传参时进行类型转换
   myThread3.join();  

    cout << "主线程的id是: " << std::this_thread::get_id() << endl;
    cout << "I Love China!" << endl;
    return 0;

}

在这里插入图片描述

  • 在主线程中进行类型转换
  • 如果在函数入口不使用引用
void myPrint3(const int &i, const A mysecond)

在这里插入图片描述

则会多进行一次拷贝构造

结论2

  • 函数形参传递的过程中使用隐式类型转换使得,mybuf和pmybuf的地址不同,这样即使主线程先执行完,释放了mybuf的地址空间,子线程中的pmybuf也能正常使用。
  • 虽然隐式类型转换的pmybuf和mybuf的地址不同,但是主线程在完成类型转换之前就将mybuf的地址释放了(即隐式类型转换是在子线程中完成的而不是在主线程中完成的),所以子线程中的pmybuf仍然不能正常使用。
  • 隐式类型转换是在子线程中完成的,如果主线程提前结束,子线程中仍然不能使用到主线程的变量值

结论

  • 传递int这种简单的类型,建议使用值传递
  • 如果传递类对象则避免使用隐式类型转换,在函数形参中,尽量使用引用来接,否则还会创建出一个新的对象
  • 终极结论:不建议使用detach()

临时对象作为线程参数

  • 在子线程中修改参数
class A{
    public:
    mutable int m_i;     //mutable表示可以修改。&m_i = 0x62fe0c, *&m_i = 0
    A(int a):m_i(a) { 
        cout << "[A::A(int a)构造函数执行]" << endl; 
        cout << "类A的构造函数的线程id : " << std::this_thread::get_id() << endl;
        } //类型转换构造函数,将整型a转换为类A的对象m_i; 将传入的i的值赋给m_i。  &i = 0x62fde8, *&i = 10
    A(const A &a):m_i(a.m_i) {
        cout << "[A::A(const A)拷贝构造函数执行]" << endl;
        cout << "类A的拷贝构造函数的线程id : " << std::this_thread::get_id() << endl;
    }
    ~A() {
        cout << "[A::~A()析构函数执行]" << endl;
        cout << "类A的析构函数的线程id : " << std::this_thread::get_id() << endl;
        }
};
  • 线程入口函数虽然为引用,但是在子线程中修改参数并不会影响主线程
  • 类A中声明对象时使用mutable表示可修改
class A{
    public:
    mutable int m_i;     //mutable表示可以修改。
    A(int a):m_i(a) { 
        cout << "[A::A(int a)构造函数执行]" << endl; 
        cout << "类A的构造函数的线程id : " << std::this_thread::get_id() << endl;
        } //类型转换构造函数,将整型a转换为类A的对象m_i; 将传入的i的值赋给m_i。  &i = 0x62fde8, *&i = 10
    A(const A &a):m_i(a.m_i) {
        cout << "[A::A(const A)拷贝构造函数执行]" << endl;
        cout << "类A的拷贝构造函数的线程id : " << std::this_thread::get_id() << endl;
    }
    ~A() {
        cout << "[A::~A()析构函数执行]" << endl;
        cout << "类A的析构函数的线程id : " << std::this_thread::get_id() << endl;
        }
};
void myPrint4(const A &pmybuf){   //  &pmybuf = 0x7a1e88
    pmybuf.m_i = 100;
    cout << "子线程myPrint4的参数const A &pmybuf的地址 = " << &pmybuf << "\nthread id = " << std::this_thread::get_id() << endl;
}
int main(){
   A myObj(10); //创建一个A类对象myObj,将10作为实参传入函数A(int i) &myObj = 0x62fe0c mi = 0 ,10
   thread myThread4(myPrint4, myObj);
    cout << "主线程的id是: " << std::this_thread::get_id() << endl;
    cout << "I Love China!" << endl;
    return 0;
}

在这里插入图片描述

  • 线程入口函数中&pmybuf = 0x7a1e88
  • 主线程中 &myObj = 0x62fe0c
  • 地址不同,修改子线程中参数并不会影响主线程
 thread myThread4(myPrint4, std::ref(myObj));

在这里插入图片描述

  • 使用std::ref会生成一个真引用,使得子线程中pmybuf的地址和主线程中地址相同

使用智能指针作为参数

//在主线程中构建智能指针
//使用std::move()将智能指针传入线程函数的形参,使得子线程的智能指针形参和主线程的智能指针指向同一地址
 unique_ptr<int> mya (new int(2) );
 thread myThread4(myPrint5, std::move(mya));

//使用智能指针作为参数
void myPrint5(unique_ptr<int> a)
  • 如果主线程执行结束释放了智能指针的内存,则子线程中的智能指针形参可能会出错

使用成员函数指针作为线程入口函数

//在类A中定义成员函数void thread_work
    void thread_work(int n){
        cout << "类A的成员函数作为线程函数的入口,线程id = " << std::this_thread::get_id() << endl; 
        cout << "类A的成员函数的this指针 = " << this << endl;
    }
//在主线程中
   A myobj(10);
   thread mytobj(&A::thread_work, myobj, 5);	//第一个参数为类A的成员函数的地址作为线程的入口,第二个参数为类A的对象,第三个参数为线程入口成员函数的形参
   mytobj.join();

在这里插入图片描述

  1. 首先执行 A myobj(10);构造一个类A对象
  2. 在主线程中执行拷贝构造函数,对myobj进行拷贝构造,所以即使使用detach()也不会出问题
  3. 在主线程中进行拷贝,在子线程中进行析构
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值