目录
传递零时对象作为线程参数
#include <iostream>
#include <thread>
using namespace std;
void myprint(const int& i,char* pmybuf)
{
printf("&i=%p\n",&i); // i 虽然是引用,但是是值拷贝
cout<<i<<endl;
cout<<pmybuf<<endl; //pmybuf 和主线程的mybuf地址是一样的。
return ;
}
int main(){
int mvar=1;
char mybuf[] = "this is a test!";
printf("&mvar=%p\n",&mvar);
thread myobj(myprint,mvar,mybuf);
//myobj.join();
myobj.detach();
cout<<"I Love China!"<<endl;
return 0;
}
//g++ -g test.cpp -std=c++11 -lpthread -o test.out
//./test.out
- 用detach()的时候,参数一定不能传指针,用指针肯定会出问题,因为主线程运行完了之后,对象的内存已经被系统回收了,但是子线程还去使用这个对象。
- 用detach()的时候,最好不要传引用,虽然引用是值拷贝的方式进行传递。
#include <iostream>
#include <thread>
using namespace std;
class A
{
public:
int m_i;
A(int a):m_i(a) {cout<<"A::A(int a) constructor is doing..."<<endl;}
A(const A& a):m_i(a.m_i) {cout<<"A::(A const& a) copy construct is doing..."<<endl;}
~A() {cout<<"A::~A() deconstructor is doing..."<<endl;}
};
void myprint(const int& i,const A& pmybuf)
{
cout<<&pmybuf<<endl;
return ;
}
int main(){
int mvar=1;
//thread myobj(myprint,mvar,A(mvar)); // 这种方式可行,但是会多执行一次拷贝构造函数。
//即使detach了,子线程会保证安全运行。
thread myobj(myprint,mvar,mvar);//这种构造方式是不安全的。
//myobj.join();
myobj.detach();
cout<<"I Love China!"<<endl;
return 0;
}
小结
- 如果传递int等简单类型,建议都是值传递,不要用应用,防止节外生枝。
- 如果传递类对象,避免隐士类型转化。全部都在创建线程这一行就构建出临时对象来,然后在函数参数里,用引用来接,否则系统还会构造出一次对象出来,避免浪费资源。
- 终极结论,建议不适用detach(),只使用join(),这样就不存在局部变量失效导致线程对内存的非法引用问题。除法万不得已,不要使用detach().
二、临时对象作为线程参数继续讲,用测试大法
线程id
1.为什么构造临时对象就安全了?探索根本原因,知其然,而知其所以然。
2.1 线程id,每个线程实际上都对应的是一个数字,不同的线程所对应的数字肯定是不一样的,线程id通过std::this_thread::get_id()来获取。
#include <iostream>
#include <thread>
using namespace std;
class A
{
public:
int m_i;
A(int a):m_i(a) {cout<<"A::A(int a) constructor is doing,this="<<this<<",threadid="<<this_thread::get_id()<<endl;}
A(const A& a):m_i(a.m_i) {cout<<"A::(A const& a) copy construct is doing,this="<<this<<",threadid="<<this_thread::get_id()<<endl;}
~A() {cout<<"A::~A() deconstructor is doing,this="<<this<<",threadid="<<this_thread::get_id()<<endl;}
};
void myprint2(const A&pmybuf)
{
cout<<"&pmybuf:"<<&pmybuf<<",threadid="<<std::this_thread::get_id()<<endl;
}
int main(){
int mvar=1;
//thread myobj(myprint,mvar,A(mvar)); //
//thread myobj(myprint,mvar,mvar);
cout<<"main thread id is:"<<std::this_thread::get_id()<<endl;
//thread myobj(myprint2,mvar);
thread myobj(myprint2,A(mvar));
myobj.join();
//myobj.detach();
cout<<"I Love China!"<<endl;
return 0;
}
wj@wj:~/WORKING_DIRECTORY/Learning/cpp_learning$ g++ -g test.cpp -std=c++11 -lpthread -o test.out
wj@wj:~/WORKING_DIRECTORY/Learning/cpp_learning$ ./test.out
main thread id is:140037806438208
A::A(int a) constructor is doing,this=0x7fff3f4d5db8,threadid=140037806438208
A::(A const& a) copy construct is doing,this=0x7fff3f4d5d70,threadid=140037806438208
A::(A const& a) copy construct is doing,this=0x55cb701fc2c8,threadid=140037806438208
A::~A() deconstructor is doing,this=0x7fff3f4d5d70,threadid=140037806438208
A::~A() deconstructor is doing,this=0x7fff3f4d5db8,threadid=140037806438208
&pmybuf:0x55cb701fc2c8,threadid=140037806434048
A::~A() deconstructor is doing,this=0x55cb701fc2c8,threadid=140037806434048
I Love China!
thread myobj(myprint2,mvar); //这种方式是在子线程中构造A对象,如果用detach(),会出现致命的问题。
thread myobj(myprint2,A(mvar));这种方式是在主线程中构造A对象,如果用detach(),就不会出现致命的问题。
传递类对象、智能指针作为线程参数
std::ref 函数 诞生的原因:
在多线程编程中,主线程和辅线程在进行参数传递的时候,不管是进行值传递,还是引用传递,编译器为了数据安全都是进行拷贝一份。那么这样其实就违背了引用的正常语义了,那么std::ref 函数就诞生了。
#include <iostream>
#include <thread>
using namespace std;
class A
{
public:
mutable int m_i;
A(int a):m_i(a) {cout<<"A::A(int a) constructor is doing,this="<<this<<",threadid="<<this_thread::get_id()<<endl;}
A(const A& a):m_i(a.m_i) {cout<<"A::(A const& a) copy construct is doing,this="<<this<<",threadid="<<this_thread::get_id()<<endl;}
~A() {cout<<"A::~A() deconstructor is doing,this="<<this<<",threadid="<<this_thread::get_id()<<endl;}
};
void myprint2(const A& pmybuf)
{
pmybuf.m_i=199; //modify m_i,not influence main function
cout<<"&pmybuf:"<<&pmybuf<<",threadid="<<std::this_thread::get_id()<<endl;
}
int main(){
A obj(10);
cout<<"main thread id is:"<<std::this_thread::get_id()<<endl;
thread mytobj(myprint2,std::ref(obj));
mytobj.join();
cout<<"m_i = "<<obj.m_i<<endl;
cout<<"I Love China!"<<endl;
return 0;
}
写法二:
#include <iostream>
#include <thread>
using namespace std;
class A
{
public:
int m_i;
A(int a):m_i(a) {cout<<"A::A(int a) constructor is doing,this="<<this<<",threadid="<<this_thread::get_id()<<endl;}
A(const A& a):m_i(a.m_i) {cout<<"A::(A const& a) copy construct is doing,this="<<this<<",threadid="<<this_thread::get_id()<<endl;}
~A() {cout<<"A::~A() deconstructor is doing,this="<<this<<",threadid="<<this_thread::get_id()<<endl;}
};
void myprint2( A& pmybuf) //此处不加const,必须要将成员变量m_i 的mutable去掉
{
pmybuf.m_i=199; //modify m_i,not influence main function
cout<<"&pmybuf:"<<&pmybuf<<",threadid="<<std::this_thread::get_id()<<endl;
}
int main(){
A obj(10);
cout<<"main thread id is:"<<std::this_thread::get_id()<<endl;
thread mytobj(myprint2,std::ref(obj));
mytobj.join();
cout<<"m_i = "<<obj.m_i<<endl; // 199
cout<<"I Love China!"<<endl;
return 0;
}
用了std::ref 之后,用引用进行参数传递的时候,就符合了引用的本来的语义了。
线程中传递智能指针写法:
#include <iostream>
#include <thread>
using namespace std;
class A
{
public:
int m_i;
A(int a):m_i(a) {cout<<"A::A(int a) constructor is doing,this="<<this<<",threadid="<<this_thread::get_id()<<endl;}
A(const A& a):m_i(a.m_i) {cout<<"A::(A const& a) copy construct is doing,this="<<this<<",threadid="<<this_thread::get_id()<<endl;}
~A() {cout<<"A::~A() deconstructor is doing,this="<<this<<",threadid="<<this_thread::get_id()<<endl;}
};
void myprint2(unique_ptr<int> pzn)
{
}
int main(){
unique_ptr<int> myp(new int(100));
std::thread mytobj(myprint2,std::move(myp));
mytobj.join(); //此处一定不能用detach(),一定要用join(),因为用detach(),new int(100) 这块内存很可能被系统回收了,那么就不能够转移到 pzn 对象了。
cout<<"I Love China!"<<endl;
return 0;
}
如果不用,std::move(myp) 就会编译不过。
std::move(myp) 表示 独占式智能指针,从主函数的myp对象转移到 子线程函数,中的 unique_ptr<int> pzn,pzn对象中。
成员函数指针做线程函数
#include <iostream>
#include <thread>
using namespace std;
class A
{
public:
int m_i;
A(int a):m_i(a) {cout<<"A::A(int a) constructor is doing,this="<<this<<",threadid="<<this_thread::get_id()<<endl;}
A(const A& a):m_i(a.m_i) {cout<<"A::(A const& a) copy construct is doing,this="<<this<<",threadid="<<this_thread::get_id()<<endl;}
~A() {cout<<"A::~A() deconstructor is doing,this="<<this<<",threadid="<<this_thread::get_id()<<endl;}
void thread_work(int num)
{
cout<<"thread_work() is doing,this="<<this<<","<<"num="<<num<<",this_thread::get_id()::"<<this_thread::get_id()<<endl;
}
};
void myprint2(unique_ptr<int> pzn)
{
}
int main(){
//unique_ptr<int> myp(new int(100));
//std::thread mytobj(myprint2,std::move(myp));
//mytobj.join();
A myobj(10);
std::thread mytobj(&A::thread_work,myobj,27);
//std::thread mytobj(&A::thread_work,std::ref(myobj),27);
mytobj.join();
cout<<"I Love China!"<<endl;
return 0;
}
//std::thread mytobj(&A::thread_work,std::ref(myobj),27); 改成这样就不会出现拷贝构造。
//std::thread mytobj(&A::thread_work,&myobj,27);
std::ref(myobj) 等价写法:&myobj
#include <iostream>
#include <thread>
using namespace std;
class A
{
public:
int m_i;
A(int a):m_i(a) {cout<<"A::A(int a) constructor is doing,this="<<this<<",threadid="<<this_thread::get_id()<<endl;}
A(const A& a):m_i(a.m_i) {cout<<"A::(A const& a) copy construct is doing,this="<<this<<",threadid="<<this_thread::get_id()<<endl;}
~A() {cout<<"A::~A() deconstructor is doing,this="<<this<<",threadid="<<this_thread::get_id()<<endl;}
void thread_work(int num)
{
cout<<"thread_work() is doing,this="<<this<<","<<"num="<<num<<",this_thread::get_id()::"<<this_thread::get_id()<<endl;
}
void operator() (int num)
{
cout<<"thread_work() is doing,this="<<this<<","<<"num="<<num<<",this_thread::get_id()::"<<this_thread::get_id()<<endl;
}
};
int main(){
//unique_ptr<int> myp(new int(100));
//std::thread mytobj(myprint2,std::move(myp));
//mytobj.join();
A myobj(10);
thread mytobj(std::ref(myobj),29); // not call copy construct
//thread mytobj(&myobj,29); //error
mytobj.join();
cout<<"I Love China!"<<endl;
return 0;
}