陷阱:当子线程函数参数使用指针时,实参与形参的地址相同。当使用detach()且主线程执行完毕,释放实参,此时形参会指向未知内存。
//myPrint1---参数为引用和指针---不推荐用,当detach时会出现问题。
void myPrint1(const int &i, char *pBuf)//待修改···
{
std::cout<
修改:
//myhPrint2---相对安全
void myPrint2(const int i, const string &pBuf)//修改
{
std::cout<
陷阱:存在myBuf已经被系统回收了(main函数执行完了),系统才用myBuf转string.
//myhPrint2---相对安全
void myPrint2(const int i, const string &pBuf)
{
std::cout<
修改:
//myhPrint2---相对安全
//1.参数中,虽然是使用引用,但是仍然会调用拷贝构造函数,进行传值,而不是传引用。
//2.若使用引用作为参数,则必须使用const,否则报错。
void myPrint2(const int i, const string &pBuf)
{
std::cout<
在创建线程的同时,构造临时对象
的方法传递参数是可以的。此方法一定能在主线程执行完毕前,将临时对象构造出来并进行值
传递。从而确保使用detach()时,子线程也能安全执行。
① 若传递int,double等简单类型参数,建议都是值传递,不要使用引用;
② 如果传递string等类对象,避免隐式对象转换,而应该在创建子线程时,就显示构建临时对象。且在函数参数要用引用来接受,否则系统还会再构造一次临时对象出来,造成资源浪费。
③ 建议不适用detach(),只使用join(),这样就不存在局部变量失效导致线程对内存的非法引用问题。
不同的线程,其线程id必然不相同。
线程id可以用C++标准库的函数来获取,std::this_thread::get_id()
来获取。
情况①:利用线程id分析上面为什么在主函数中创建线程对象时,线程参数要显式类型转换?
class A
{
public:
int m_i;
A(int a):m_i(a)
{
std::cout<<"A::A(int a)构造函数的线程id"<<"\t"<< std::this_thread::get_id()<
执行结果如下:
分析:可以看到,执行构造函数和拷贝构造函数的线程ID与主线程线程ID一致,也就意味着,当显示进行类类型转换时,会在主线程执行完毕之前,进行子线程的值传递。
如果线程参数采用隐式转换,执行结果如下:
可以看到,构造函数的线程ID与主线程的线程ID不一致,当使用detach()时,就会出现主线程执行完毕,但是子线程中的参数传递错误问题。
情况②:当线程参数用const修饰时,是传值还是传引用?
class A
{
public:
mutable int m_i;
A(int a) :m_i(a)
{
std::cout << "A::A(int a)构造函数的线程ID" << "\t" << this <<"\t"<< std::this_thread::get_id() << std::endl;
}
A(const A &a) :m_i(a.m_i)
{
std::cout << "A::A(const A)拷贝构造函数的线程ID" << "\t" << this << "\t" << std::this_thread::get_id() << std::endl;
}
~A()
{
std::cout << "A::~A()析构函数的线程ID" << "\t" << this << "\t" << std::this_thread::get_id() << std::endl;
}
};
void myPrint3(const A &pmyBuf)
{
pmyBuf.m_i = 2000;
std::cout << "子线程myPrint3的参数地址为" << &pmyBuf << "线程ID = " << std::this_thread::get_id() << std::endl;
}
int main()
{
std::cout << "主线程的ID:" << std::this_thread::get_id() << std::endl;
A a(1);
std::thread mytobj(myPrint3, a);//mytobj将类对象作为线程参数
mytobj.join();
//mytobj.detach();
return 0;
}
分析:用mutable
修饰成员变量m_i
使其在const的情况下也可以修改,从执行结果可以看到,形参和实参的地址以及值均不相同,也就意味着在子线程参数虽然为引用,但依然是传值
。
这样就出现一个问题:如果想要在子线程中修改成员变量,且发挥引用的效果,该怎么处理?
class A
{
public:
int m_i;
A(int a) :m_i(a)
{
std::cout << "A::A(int a)构造函数的线程ID" << "\t" << this <<"\t"<< std::this_thread::get_id() << std::endl;
}
A(const A &a) :m_i(a.m_i)
{
std::cout << "A::A(const A)拷贝构造函数的线程ID" << "\t" << this << "\t" << std::this_thread::get_id() << std::endl;
}
~A()
{
std::cout << "A::~A()析构函数的线程ID" << "\t" << this << "\t" << std::this_thread::get_id() << std::endl;
}
};
void myPrint3(A &pmyBuf)//此处可以去掉const修饰
{
pmyBuf.m_i = 2000;
std::cout << "子线程myPrint3的参数地址为" << &pmyBuf << "线程ID = " << std::this_thread::get_id() << std::endl;
}
int main()
{
std::cout << "主线程的ID:" << std::this_thread::get_id() << std::endl;
A a(1);
std::thread mytobj(myPrint3, std::ref(a));//用std::ref修饰即可
mytobj.join();
//mytobj.detach();
return 0;
}
结果如下:
可以看出,利用std::ref()
修饰后,形参和实参的地址相等,值也相等。
另外,此时也不会再执行拷贝构造函数了(当然了,因为上面都说了是利用引用传参嘛)。
class A
{
public:
mutable int m_i;
A(int a) :m_i(a)
{
std::cout << "A::A(int a)构造函数的线程ID" << "\t" << this <<"\t"<< std::this_thread::get_id() << std::endl;
}
A(const A &a) :m_i(a.m_i)
{
std::cout << "A::A(const A)拷贝构造函数的线程ID" << "\t" << this << "\t" << std::this_thread::get_id() << std::endl;
}
~A()
{
std::cout << "A::~A()析构函数的线程ID" << "\t" << this << "\t" << std::this_thread::get_id() << std::endl;
}
};
void myPrint3(unique_ptr pmyBuf)
{
std::cout << "子线程myPrint3的参数地址为" << &pmyBuf << "线程ID = " << std::this_thread::get_id() << std::endl;
}
int main()
{
std::cout << "主线程的ID:" << std::this_thread::get_id() << std::endl;
unique_ptr myptr(new int(100));
std::thread mytobj(myPrint3,move(myptr));//用move将内存绑定到形参pmyBuf上
mytobj.join();
//mytobj.detach();
getchar();
return 0;
}
class A
{
public:
mutable int m_i;
A(int a) :m_i(a)
{
std::cout << "A::A(int a)构造函数的线程ID" << "\t" << this <<"\t"<< std::this_thread::get_id() << std::endl;
}
A(const A &a) :m_i(a.m_i)
{
std::cout << "A::A(const A)拷贝构造函数的线程ID" << "\t" << this << "\t" << std::this_thread::get_id() << std::endl;
}
~A()
{
std::cout << "A::~A()析构函数的线程ID" << "\t" << this << "\t" << std::this_thread::get_id() << std::endl;
}
void thread_work(int num)
{
std::cout << "子线程thread_work开始执行" << "\t" << this << "\t" << std::this_thread::get_id() << std::endl;
}
};
int main()
{
std::cout << "主线程的ID:" << std::this_thread::get_id() << std::endl;
A myobj(10);
std::thread mytobj(&A::thread_work, &myobj, 15);//此处第二个参数用了引用,就不要再用detach()了。
mytobj.join();
getchar();
return 0;
}