对于带参的可执行对象的参数的传递的思考
值是如何传递的
值是通过拷贝的形式传递的
#include<iostream>
#include<string>
#include<thread>
using namespace std;
void myPrint(int i, char* s)
{
cout<<i<<endl;
cout<<&s<<endl;
}
int main()
{
int i = 10;
char s[] = "I have a Pen!";
cout<<&s<<endl;
thread firstThread(myPrint, i, s);
firstThread.join();
cout<<"这里是主线程"<<endl;
return 1;
}
可以发现两者的地址不同,即为拷贝传递值
使用detach会不会出错
既然是通过拷贝的形式传递,那么在主线程中创建的对象,如果在主线程执行结束后被释放了,但是此时子线程还没有进行,那么传递参数的时候就会出现错误
#include<iostream>
#include<string>
#include<thread>
using namespace std;
void myPrint(int i, char* s)
{
cout<<i<<endl;
cout<<"子线程中的地址>:"<<&s<<endl;
}
int main()
{
int i = 10;
char s[] = "I have a Pen!";
cout<<"主线程中的地址>:"<<&s<<endl;
thread firstThread(myPrint, i, s);
firstThread.detach();
cout<<"这里是主线程"<<endl;
return 1;
}
程序的执行结果可能是这样,在子线程中的执行结果没有进行打印
当子线程接收的参数是指针时,就会引发空指针异常,导致程序的崩溃
如何解决问题
- 既然会导致空指针异常,那么我们可以不使用指针,在上面的函数中的第二个参数的类型设置为string不不就可以了吗?
但是经过尝试,这样依然是不可行的
如果在主线程结束后,子线程才执行那么,子线程就会传递一个被释放的对象,这样导致的结果是不可以估量的。
- 我们可以尝试在创建线程的时候就将要传递的值转换成目标对象,然后再进行传递
#include<iostream>
#include<string>
#include<thread>
using namespace std;
class Obj{
private:
int m_i;
public:
void operator()(){};
Obj(const int& i):m_i(i){};
Obj(const Obj& obj):m_i(obj.m_i){cout<<this<<"拷贝构造函数执行"<<endl;};
~Obj(){ cout<<this<<"析构函数执行"<<endl;}
};
void myPrint(int i, const Obj &obj)
{
cout<<i<<endl;
cout<<"子线程中的地址>:"<<&obj<<endl;
}
int main()
{
int i = 10;
// cout<<"主线程中的地址>:"<<&s<<endl;
int cur = 12;
thread firstThread(myPrint, i, Obj(cur));
firstThread.detach();
cout<<"这里是主线程"<<endl;
return 1;
}
实验证明,这样实现是正确的
验证方法
每一个线程执行都会创建一个线程id,如果传递的变量是再子线程中被拷贝的,这样就一定会引发错误
所以我们只需要验证,传递参数是再主线程中进行的还是再子线程中进行的即可
#include<iostream>
#include<string>
#include<thread>
using namespace std;
class Obj{
private:
int m_i;
public:
void operator()(){};
Obj(const int i):m_i(i){cout<<this<<"构造函数执行 线程ID为>:"<<this_thread::get_id()<<endl;};
Obj(const Obj& obj):m_i(obj.m_i){cout<<this<<"拷贝构造函数执行 线程ID为>:"<<this_thread::get_id()<<endl;};
~Obj(){ cout<<this<<"析构函数执行 线程ID为>:"<<this_thread::get_id()<<endl;}
};
void myPrint(int i, const Obj &obj)
{
cout<<i<<endl;
cout<<"子线程中的地址>:"<<&obj<<"线程ID为>:"<<this_thread::get_id()<<endl;
}
int main()
{
int i = 10;
cout<<"主线程ID为>:"<<this_thread::get_id()<<endl;
int cur = 12;
thread firstThread(myPrint, i, cur);
firstThread.detach();
cout<<"这里是主线程"<<endl;
return 1;
}
这个是隐式类型转换,按照我们上面的猜想,这个会在子线程中才进行拷贝构造
证明确实如此,构造函数都在子线程中进行了
#include<iostream>
#include<string>
#include<thread>
using namespace std;
class Obj{
private:
int m_i;
public:
void operator()(){};
Obj(const int i):m_i(i){cout<<this<<"构造函数执行 线程ID为>:"<<this_thread::get_id()<<endl;};
Obj(const Obj& obj):m_i(obj.m_i){cout<<this<<"拷贝构造函数执行 线程ID为>:"<<this_thread::get_id()<<endl;};
~Obj(){ cout<<this<<"析构函数执行 线程ID为>:"<<this_thread::get_id()<<endl;}
};
void myPrint(int i, const Obj obj)
{
cout<<"子线程中的地址>:"<<&obj<<"线程ID为>:"<<this_thread::get_id()<<endl;
}
int main()
{
int i = 10;
cout<<"主线程ID为>:"<<this_thread::get_id()<<endl;
int cur = 12;
thread firstThread(myPrint, i, Obj(cur));
firstThread.join();
cout<<"这里是主线程"<<endl;
return 1;
}
当以显式类型转换时,按照上面的推测,会在主线程中执行拷贝构造函数
结果确实如此
线程共用对象
当我们传递对象时,时通过拷贝的形式传递的,但是如果想要两个线程操作同一个对象需要怎么办呢?
这就需要用到std::ref()
函数
#include"main.h"
class Obj{
public:
mutable int m_i;
Obj(int i): m_i(i){};
};
void Print(const Obj& obj)
{
obj.m_i = 160 ;
cout<<this_thread::get_id()<<">: "<<&obj<<endl;
}
int main()
{
Obj obj(10);
cout<<this_thread::get_id()<<">: "<<&obj<<endl;
thread myThread(Print, obj);
myThread.join();
return 1;
}
不加ref
#include"main.h"
class Obj{
public:
mutable int m_i;
Obj(int i): m_i(i){};
};
void Print(const Obj& obj)
{
obj.m_i = 160 ;
cout<<this_thread::get_id()<<">: "<<&obj<<endl;
}
int main()
{
Obj obj(10);
cout<<this_thread::get_id()<<">: "<<&obj<<endl;
thread myThread(Print, ref(obj));
myThread.join();
return 1;
}
加了ref后
传递智能指针
我们知道,智能指针会在线程用不到它的时候自动释放,那如何传递智能指针?
要知道这一点,就不能使用detach(),这样会导致智能指针被释放,导致子线程在使用的时候会导致空指针异常
#include"main.h"
void print(unique_ptr<int> ptr)
{
cout<<this_thread::get_id()<<">: "<<&ptr<<endl;
}
int main()
{
unique_ptr<int> ptr(new int(100));
cout<<this_thread::get_id()<<">: "<<&ptr<<endl;
thread myThread(print, move(ptr));
myThread.join();
return 1;
}
unique_ptr不支持拷贝,所以要使用std::move()
来传递指针