#include <iostream>
#include <thread>
using namespace std;
// void myprint(const int &argi, char *pch)
// void myprint(const int argi, char *pch)
void myprint(const int argi, const string &pch) // 发生隐式转换
{
cout << argi << endl; // 分析认为:argi并不是外部i的引用,实际是值传递,则认为即使th执行了detach,那么子线程中用i值仍然是安全的
// cout << pch << endl; // 指针在detach子线程时,绝对是会有问题的
cout << pch.c_str() << endl; // 指针在detach子线程时,绝对是会有问题的
}
int main()
{
// 一: 创建临时对象作为线程参数
//(1.1)要避免的陷阱(解释1)
int i = 1;
int &ri = i;
char ch[] = "hello world";
// 因为是引用 i和&ri的地址是一样的
thread th(myprint, ri, ch); // 形参是指针 数组名也是指针,指向同一地址
// th.join();
th.detach(); // 使用detach的时候,不推荐用引用,绝对不能用指针
cout << "end main" << endl;
return 0;
}
上面代码运行时正常的,看似没有问题,
但是传参的时候,ch到底是什么时候转成string的,
如果main函数执行完,你再转成string,那也就是有问题的,因为这时候ch已经被系统回收了,再转的结果就是不可预料
事实上存在,ch都被回收了(main函数执行完了),系统才用ch去转string的可能性
经过查资料,最终应该改成
thread th(myprint, ri, string(ch));//临时构造一个string对象作为实参传递,不用发生隐式转换
那么如何确定string(ch)确实是在main函数结束前执行的呢?
下面通过一个自定义类来验证
#include <iostream>
#include <thread>
using namespace std;
class A
{
public:
int m_i;
A(int a) : m_i(a) { cout << "构造函数执行" << endl; }
A(const A &a) : m_i(a.m_i) { cout << "拷贝构造函数执行" << endl; }
// A(A &&a) : m_i(a.m_i) { cout << "移动函数执行" << endl; }
~A() { cout << "析构函数执行" << endl; }
};
void myprint(const int argi, const A &a)
{
cout << &a << endl;
}
int main()
{
int i = 1;
int j = 10;
// thread th(myprint, i, j); // 传j,调用构造函数A(int a) 将一个数字转为A对象,执行程序的时候,构造函数没来得及执行,说明构造是在子线程中完成
thread th(myprint, i, A(j)); // 匿名对象,构造函数是在主线程中完成,然后调用拷贝构造生成一个副本,这个副本才是按照引用传递给myprint的实参,这个也是在主线程中完成的
// thread th(myprint, i, A(j)); //在创建线程的同事构造临时对象的方法传递参数是可行的
th.detach();
// th.join();
cout << "end main" << endl;
return 0;
}
/*
thread的参数是右值引用,上例中A没有移动构造,传参的时候找不到移动构造,就会找拷贝构造
*/
再测试下
#include <iostream>
#include <thread>
using namespace std;
class A
{
public:
int m_i;
A(int a) : m_i(a) { cout << "构造函数执行,thread_id:" << this_thread::get_id() << endl; }
A(const A &a) : m_i(a.m_i) { cout << "拷贝构造函数执行,thread_id:" << this_thread::get_id() << endl; }
// A(A &&a) : m_i(a.m_i) { cout << "移动函数执行" << endl; }
~A() { cout << "析构函数执行,thread_id:" << this_thread::get_id() << endl; }
};
void myprint(const A &a)
{
cout << &a << ' ' << this_thread::get_id() << endl;
}
int main()
{
cout << "主线程id:" << this_thread::get_id() << endl;
int j = 10;
// thread th(myprint, A(j));
thread th(myprint, j);
th.join();
// th.detach();
cout << "end main" << endl;
return 0;
}
两种情况
1:使用 thread th(myprint, A(j));
主线程id:1
构造函数执行,thread_id:1
拷贝构造函数执行,thread_id:1
析构函数执行,thread_id:1
0x1b601f026a8 2
析构函数执行,thread_id:2
end main
2:使用 thread th(myprint, j);
主线程id:1
构造函数执行,thread_id:2
0x68b65ffb2c 2
析构函数执行,thread_id:2
end main
再进行测试,把myprint改成非引用
#include <iostream>
#include <thread>
using namespace std;
class A
{
public:
int m_i;
A(int a) : m_i(a) { cout << "构造函数执行,thread_id:" << this_thread::get_id() << endl; }
A(const A &a) : m_i(a.m_i) { cout << "拷贝构造函数执行,thread_id:" << this_thread::get_id() << ",&a=" << &a << ",&this:" << this << endl; }
// A(A &&a) : m_i(a.m_i) { cout << "移动函数执行" << endl; }
~A() { cout << "析构函数执行,thread_id:" << this_thread::get_id() << endl; }
};
void myprint(const A &a)
{
cout << &a << ' ' << this_thread::get_id() << endl;
}
int main()
{
cout << "主线程id:" << this_thread::get_id() << endl;
int j = 10;
thread th(myprint, A(j));
// thread th(myprint, j);
th.join();
cout << "end main" << endl;
return 0;
}
1:使用 void myprint(const A &a);
主线程id:1
构造函数执行,thread_id:1
拷贝构造函数执行,thread_id:1,&a=0xef1bdff5f8,&this:0x25f927803a8
析构函数执行,thread_id:1
0x25f927803a8 2
析构函数执行,thread_id:2
end main
2:使用 void myprint(const A a);
主线程id:1
构造函数执行,thread_id:1
拷贝构造函数执行,thread_id:1,&a=0x67a15ff5f8,&this:0x1e00aaa4758
析构函数执行,thread_id:1
拷贝构造函数执行,thread_id:2,&a=0x1e00aaa4758,&this:0x67a1dff88c
0x67a1dff88c 2
析构函数执行,thread_id:2
析构函数执行,thread_id:2
end main
可以看出是拷贝一份到子线程,然后如果myprint的形参是用引用,则直接使用这份拷贝的数据,如果不是引用,是A对象接收,则会将该副本再拷贝一次。
传递类对象、智能指针作为线程参数
std::ref()函数
#include <iostream>
#include <thread>
using namespace std;
class A
{
public:
mutable int m_i;
A(int a) : m_i(a) { cout << "构造函数执行,thread_id:" << this_thread::get_id() << endl; }
A(const A &a) : m_i(a.m_i) { cout << "拷贝构造函数执行,thread_id:" << this_thread::get_id() << endl; }
// A(A &&a) : m_i(a.m_i) { cout << "移动函数执行" << endl; }
~A() { cout << "析构函数执行,thread_id:" << this_thread::get_id() << endl; }
};
void myprint(const A &a)//这里const不能去掉,个人觉得,因主线程中是值传递,这里如果可以修改,也是修改拷贝过来的内容,没有任何意义,所以编译器,要求传值时要const,传引用就不要const了
{
a.m_i = 1111;
cout << &a << ' ' << this_thread::get_id() << endl;
}
int main()
{
cout << "主线程id:" << this_thread::get_id() << endl;
A a(10);
thread th(myprint, a);
th.join();
// th.detach();
cout << "end main" << ' ' << a.m_i << endl;
return 0;
}
主线程id:1
构造函数执行,thread_id:1
拷贝构造函数执行,thread_id:1
0x2f1a22fe9f8 2
析构函数执行,thread_id:2
end main 10
析构函数执行,thread_id:1
上面代码是修改备份中的内容,所以主线程中的a不会被修改。
如果修改,可以使用ref传引用
#include <iostream>
#include <thread>
using namespace std;
class A
{
public:
int m_i;
A(int a) : m_i(a) { cout << "构造函数执行,thread_id:" << this_thread::get_id() << endl; }
A(const A &a) : m_i(a.m_i) { cout << "拷贝构造函数执行,thread_id:" << this_thread::get_id() << endl; }
// A(A &&a) : m_i(a.m_i) { cout << "移动函数执行" << endl; }
~A() { cout << "析构函数执行,thread_id:" << this_thread::get_id() << endl; }
};
void myprint(A &a)
{
a.m_i = 1111;
cout << &a << ' ' << this_thread::get_id() << endl;
}
int main()
{
cout << "主线程id:" << this_thread::get_id() << endl;
A a(10);
thread th(myprint, ref(a));
th.join();
// th.detach();
cout << "end main" << ' ' << a.m_i << endl;
return 0;
}
主线程id:1
构造函数执行,thread_id:1
0xb5d03ff994 2
end main 1111
析构函数执行,thread_id:1
传递智能指针
#include <iostream>
#include <thread>
using namespace std;
class A
{
public:
int m_i;
A(int a) : m_i(a) { cout << "构造函数执行,thread_id:" << this_thread::get_id() << endl; }
A(const A &a) : m_i(a.m_i) { cout << "拷贝构造函数执行,thread_id:" << this_thread::get_id() << endl; }
// A(A &&a) : m_i(a.m_i) { cout << "移动函数执行" << endl; }
~A() { cout << "析构函数执行,thread_id:" << this_thread::get_id() << endl; }
};
void myprint(A &a)
{
a.m_i = 1111;
cout << &a << ' ' << this_thread::get_id() << endl;
}
void myprint2(unique_ptr<int> uptr)
{
cout << this_thread::get_id() << endl;
}
int main()
{
cout << "主线程id:" << this_thread::get_id() << endl;
// A a(10);
// thread th(myprint, ref(a));
thread th(myprint2, unique_ptr<int>(new int(100)));
// unique_ptr<int> muptr(new int(100));
// thread th(myprint2, move(muptr));//unique_ptr已经删除了拷贝构造,所以这里得用move
th.join();
// th.detach();
cout << "end main" << endl;
return 0;
}