03C++11多线程编程之测试join,detach传各种实参时形参的拷贝次数
首先我们看下面的总结测试图,然后一步步的测试。
1 这里我们先测试join传实参的类型
当实参为普通对象时:
1)当形参为普通对象时,拷贝了两次。
2)当形参为引用时:拷贝了一次。
3)当形参为指针时:无拷贝。
当实参为临时对象时:
1)形参为值时,拷贝了两次。
2)形参为引用时,拷贝了一次,并且在主线程拷贝构造完毕。
3)当形参为指针时:无拷贝。
当实参为引用时:
1)当形参为普通对象时:拷贝一次。
2)当形参为引用时:无拷贝。
3)当形参为指针时:无法传参。
thread mytobj(MyPrint, std::ref(a));//error
thread mytobj(MyPrint, &std::ref(a));//error
2 detach传实参的类型测试
当实参为普通对象时:
1)当形参为普通对象时:拷贝两次。
2)当形参为引用时:拷贝一次。
3)当形参为指针时:无拷贝,并且多次执行非法访问m_i,打印的值为未确定值,非常危险,禁用。
当实参为临时对象时:
1)当形参为普通对象时:拷贝两次。这里注意一下:它每次输出结果都是正确的,即10,但是因为线程打印次序有时很乱,导致1和0错开。但是你打断点查看每次都是10。
2)当形参为引用时:拷贝一次,并且如果你多次运行,发现它每次都是在主线程执行完毕就已经拷贝构造完成。这个方法也是最安全的传参方法。
3)当形参为指针时:无拷贝,并且禁用,多次运行会出现未知的值。
可以看到,没有值是因为主线程已经结束了,线程与屏幕脱离关系。所以就导致指针再访问就出现问题。
当实参为引用时:
1)当形参为普通对象时:拷贝一次。危险操作。
2)当形参为引用时:无拷贝。危险操作。
3)当形参为指针时:无拷贝。危险操作,万万不能使用。并且传参失败。
thread mytobj(MyPrint, std::ref(a));//error
thread mytobj(MyPrint, &std::ref(a));//error
3 给出代码,自己一步步的安装上面传参即可,不要偷懒
#include<iostream>
#include<thread>
#include<string>
using namespace std;
class A{
public:
mutable int m_i;
public:
//类型转换构造函数,可以把一个int转换成一个类A对象。
A(int a) :m_i(a){
cout << "A::A(int a)构造函数执行!" << this << "threadid:" << std::this_thread::get_id() << endl;
}
A(const A &a) :m_i(a.m_i){
cout << "A::A(A &a)复制构造函数执行!" << this << "threadid:" << std::this_thread::get_id() << endl;
}
~A(){
cout << "A::~A()析构函数执行!" << this << "threadid:" << std::this_thread::get_id() << endl;
}
void thread_work(int num){
cout << "子线程thread——work执行!" << this << "threadid:" << std::this_thread::get_id() << endl;
}
};
//形参a
void MyPrint(const A a) {
cout << "I am A" << endl;
//打印多行是方便测试指针在主线程结束在线程非法访问m_i
//cout << "I am A" << endl;
//cout << "I am A" << endl;
//cout << "I am A" << endl;
//cout << "I am A" << endl;
cout << a->m_i << endl;
}
int main(){
//实参为thread参数2
A a(10);
thread mytobj(MyPrint, a);
mytobj.join();
return 0;
}
4 终极总结上面join和detach如何传参
下面这四点上一篇文章也总结过了。
- 1)int型的直接值传递。
- 2)指针绝不使用。
- 3)传递类对象时,形参为引用,实参为匿名对象。形参为引用是防止多次拷贝,实参为匿名对象是防止子线程拷贝之前主线程提前结束。
- 4)使用join上面问题均不出现。
- 5)了解各种传参时的拷贝次数,拷贝次数看上图。
学完本篇,你可以熟练掌握线程如何传参。