1.传递临时对象作为线程参数
- 原始的输入程序如下:
#include <iostream>
#include <thread>
using namespace std;
void myprint(const int& i, char* pmybuf)
{
cout << i << endl;
cout << pmybuf << endl;
return;
}
int main(){
int mvar = 1;
int &mvary = mvar;
char mybuf[] = "this is a test!";
thread mytobj(myprint, mvar, mybuf);
mytobj.join();
cout << "I Love China!" << endl;
return 0;
}
1.1 要避免的陷阱1
#include <iostream>
#include <thread>
using namespace std;
void myprint(const int& i, char* pmybuf)
{
cout << i << endl; // 分析认为,i并不是mvar的引用,实际是值传递,那么我们认为,即使主线程detach了子线程,那么子线程中用i值仍然是安全的!
cout << pmybuf << endl; // 指针在detach子线程时,绝对会有问题!
return;
}
int main(){
int mvar = 1;
int &mvary = mvar;
char mybuf[] = "this is a test!";
thread mytobj(myprint, mvar, mybuf);
mytobj.detach(); // 子线程和主线程分别执行!
cout << "I Love China!" << endl;
return 0;
}
- 修改上述程序
#include <iostream>
#include <thread>
#include <string>
using namespace std;
void myprint(const int i, const string& pmybuf)
{
cout << i << endl;
cout << pmybuf.c_str() << endl;
return;
}
int main(){
int mvar = 1;
int &mvary = mvar;
char mybuf[] = "this is a test!";
thread mytobj(myprint, mvar, mybuf);
mytobj.detach();
cout << "I Love China!" << endl;
return 0;
}
1.2 要避免的陷阱2
- 但是,mybuf到底是在什么时候转换成string的?
#include <iostream>
#include <thread>
#include <string>
using namespace std;
void myprint(const int i, const string& pmybuf)
{
cout << i << endl;
cout << pmybuf.c_str() << endl;
return;
}
int main(){
int mvar = 1;
int &mvary = mvar;
char mybuf[] = "this is a test!";
thread mytobj(myprint, mvar, mybuf); // 但是mybuf到底是在什么时候转换成string的?
mytobj.detach();
cout << "I Love China!" << endl;
return 0;
}
- 结论:事实上存在,mybuf都被回收了(main函数执行完了),系统才用mybuf去转string的可能性。
- 继续修改上面的代码,可以见下面的反例程序:
#include <iostream>
#include <thread>
#include <string>
using namespace std;
void myprint(const int i, const string& pmybuf)
{
cout << i << endl;
cout << pmybuf.c_str() << endl;
return;
}
int main(){
int mvar = 1;
int &mvary = mvar;
char mybuf[] = "this is a test!";
thread mytobj(myprint, mvar, string(mybuf)); // 我们这里直接将mybuf转换成string对象,这是一个可以保证在线程中用肯定有效的对象!!!
mytobj.detach();
cout << "I Love China!" << endl;
return 0;
}
- 解释上面修改的原因见下面的程序:
#include <iostream>
#include <thread>
#include <string>
using namespace std;
class A
{
public:
int m_i;
// 类型转换构造函数,可以把一个int转换成一个类A对象
A(int a) :m_i(a)
{
cout << "A::A(int a)构造函数执行" << endl;
}
A(const A& a) : m_i(a.m_i)
{
cout << "A::A(const A& a)拷贝构造函数执行" << endl;
}
~A()
{
cout << "A::~A()析构函数执行" << endl;
}
};
void myprint(const int i, const A& pmybuf)
{
cout << &pmybuf << endl; // 这里打印的是pmybuf对象的地址
}
int main(){
int mvar = 1;
int mysecondpar = 12;
thread mytobj(myprint, mvar, mysecondpar); // 我们是希望mysecondpar转成A类型对象传递给myprint的第二个参数
mytobj.detach();
cout << "I Love China!" << endl;
return 0;
}
- thread mytobj(myprint, mvar, A(mysecondpar)),在创建线程的同时构造临时对象的方法传递参数是可行的!
- 只要用临时构造的A类对象传递给线程,那么就一定能够在主线程执行完毕前把线程函数的第二个参数构建出来,从而确保即使detach了,子线程也能安全运行了
1.3 总结
- 总结1:如果传递int这种简单类型的参数,建议都是值传递,不要用引用,防止节外生枝!
- 总结2:如果传递类对象,避免隐式类型转换。全部都在创建线程这一行就构建出临时对象来,然后在函数参数里用引用来接,否则系统还会多构造一次对象,浪费空间。
- 终极结论:建议不使用detach(),只使用join():这样就不存在局部变量失效导致线程对内存的非法引用问题。
2.临时对象作为线程参数后续
2.1 线程id概念
2.2 临时对象构造时间抓捕
3.传递类对象、智能指针作为线程参数
4.用过成员函数指针做线程函数