一、传递临时对象作为线程参数
1.要避免的陷阱(解释1)
- i 并不是真正的引用传递,所以是安全的
- mybuf 是传指针,所以是不安全的,可以将char* pmybuf改为 const string &类型,为复制的,开辟的新内存(暂时感觉没有问题)。
- 所以不推荐用引用,绝对不可以用指针
#include <thread>
#include <iostream>
using namespace std;
void my_print(const int &i, char * pmybuf)
{
cout << i << endl;
cout << pmybuf << endl;
return;
}
int main()
{
int mvar = 1;
int& myary = mvar;
char mybuf[] = "this is a test!";
thread mytobj(my_print, mvar, mybuf);
mytobj.detach();
cout << "Ilove chain !" << endl;
return 0;
}
2.要避免的陷阱(解释2)
- 有种可能是:主线程都执行完了,mybuf已经被回收了,此线程才开始,才有mybuf到pbuf的转换。事实上存在mybuf都被回收了,系统采用mybuf转pbuf的现象,就会出错。
#include <thread>
#include <iostream>
using namespace std;
void my_print(const int &i, const string &pmybuf)
{
cout << i << endl;
cout << pmybuf.c_str() << endl;
return;
}
int main()
{
int mvar = 1;
int& myary = mvar;
char mybuf[] = "this is a test!";
thread mytobj(my_print, mvar, mybuf);
mytobj.detach();
cout << "I love chain !" << endl;
return 0;
}
改为:
#include <thread>
#include <iostream>
using namespace std;
void my_print(const int &i, const string &pmybuf)
{
cout << i << endl;
cout << pmybuf.c_str() << endl;
return;
}
int main()
{
int mvar = 1;
int& myary = mvar;
char mybuf[] = "this is a test!";
thread mytobj(my_print, mvar, string(mybuf));
mytobj.detach();
cout << "I love chain !" << endl;
return 0;
}
验证
#include <thread>
#include <iostream>
using namespace std;
class A
{
public:
int m_i;
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)拷贝构造函数执行]" << endl; }
~A() { cout << "[A::A()析构函数执行]" << endl; }
};
void my_print(const int& i, const A &pmybuf)
{
cout << &pmybuf << endl;
return;
}
int main()
{
int mvar = 1;
int mysecondpar = 12;
thread mytobj(my_print, mvar, mysecondpar);
mytobj.detach();
cout << "I love chain !" << endl;
return 0;
}
尝试构造一个类A的临时对象
#include <thread>
#include <iostream>
using namespace std;
class A
{
public:
int m_i;
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)拷贝构造函数执行]" << endl; }
~A() { cout << "[A::A()析构函数执行]" << endl; }
};
void my_print(const int& i, const A &pmybuf)
{
cout << &pmybuf << endl;
return;
}
int main()
{
int mvar = 1;
int mysecondpar = 12;
thread mytobj(my_print, mvar, A(mysecondpar));
mytobj.detach();
cout << "I love chain !" << endl;
return 0;
}
3.总结
- 只要用临时构造函数作为参数的传递给线程,那么就一定能够执行完毕前把第二个参数构造出来,从而确保即便detach(),子线程也能安全运行。
主要针对 detach() - 如传递int类型参数,都是值传递,不要用引用,防止节外生枝
- 如果传递类对象,不要隐式类型转换。全部都在创建线程这一行就构建出临时对象,然后在函数参数里用引用来接,如果不用引用,否则系统还会多构造一个对象,造成浪费。
- 终极结论:建议不是用detach(),只是用join()。这样就不存在局部变量失效,线程对内存的非法使用。
二、临时对象作为线程参数继续讲
1.线程id概念
- 每个线程不管是主线程还是子线程,每个线程世纪上都对应这一个数字,并且不相同。
- 线程id可以用c++标准库里面的函数:std::this_thread::get_id()来获取。
2.临时对象构造时机抓捕
#include <thread>
#include <iostream>
using namespace std;
class A
{
public:
int m_i;
A(int a) :m_i(a)
{
cout << "[A::A(int a)构造函数执行]" << this << "---thread_id = " << std::this_thread::get_id() << endl;
}
A(const A &a) :m_i(a.m_i) { cout << "[A::A(const A)拷贝构造函数执行]" << this << "---thread_id = " << std::this_thread::get_id() << endl; }
~A() { cout << "[A::A()析构函数执行]" << this << "---thread_id = " << std::this_thread::get_id() << endl; }
};
void my_print(const int& i, const A &pmybuf)
{
cout << &pmybuf << endl;
return;
}
void my_print2(const A& pmybuf)
{
cout << "子线程 myprint 的参数地址是:" << &pmybuf << "---thread_id = " << std::this_thread::get_id() << endl;
}
int main()
{
cout << "主线程 --- thread_id = " << std::this_thread::get_id() << endl;
int mvar = 1;
int mysecondpar = 12;
thread mytobj(my_print2, mvar);
mytobj.join();
cout << "I love chain !" << endl;
return 0;
}
如果传引用:执行一次拷贝函数 如果不传引用:执行两次拷贝函数 所以传类对象,一般要用引用传;
三、传递类对象、智能指针作为线程参数
#include <thread>
#include <iostream>
using namespace std;
class A
{
public:
mutable int m_i;
A(int a) :m_i(a)
{
cout << "[A::A(int a)构造函数执行]" << this << "---thread_id = " << std::this_thread::get_id() << endl;
}
A(const A& a) :m_i(a.m_i) { cout << "[A::A(const A)拷贝构造函数执行]" << this << "---thread_id = " << std::this_thread::get_id() << endl; }
~A() { cout << "[A::A()析构函数执行]" << this << "---thread_id = " << std::this_thread::get_id() << endl; }
};
void my_print(const int& i, const A& pmybuf)
{
cout << &pmybuf << endl;
return;
}
void my_print2(const A& pmybuf)
{
pmybuf.m_i = 199;
cout << "子线程 myprint 的参数地址是:" << &pmybuf << "---thread_id = " << std::this_thread::get_id() << endl;
}
int main()
{
A myobj(10);
thread mytobj(my_print2, myobj);
mytobj.join();
return 0;
}
std::ref函数(智能指针作为参数传递)
#include <thread>
#include <iostream>
using namespace std;
class A
{
public:
mutable int m_i;
A(int a) :m_i(a)
{
cout << "[A::A(int a)构造函数执行]" << this << "---thread_id = " << std::this_thread::get_id() << endl;
}
A(const A& a) :m_i(a.m_i) { cout << "[A::A(const A)拷贝构造函数执行]" << this << "---thread_id = " << std::this_thread::get_id() << endl; }
~A() { cout << "[A::A()析构函数执行]" << this << "---thread_id = " << std::this_thread::get_id() << endl; }
};
int main()
{
unique_ptr<int> myp(new int(100));
thread mytobj(my_print2, std::move(myp));
mytobj.join();
return 0;
}
四、用成员函数指针做线程函数
#include <thread>
#include <iostream>
using namespace std;
class A
{
public:
int m_i;
A(int a) :m_i(a)
{
cout << "[A::A(int a)构造函数执行]" << this << "---thread_id = " << std::this_thread::get_id() << endl;
}
A(const A& a) :m_i(a.m_i) { cout << "[A::A(const A)拷贝构造函数执行]" << this << "---thread_id = " << std::this_thread::get_id() << endl; }
~A() { cout << "[A::A()析构函数执行]" << this << "---thread_id = " << std::this_thread::get_id() << endl; }
void thread_work(int num)
{
cout << "[子线程 thread_work 执行]" << endl;
}
};
int main()
{
A myobj(10);
std::thread mytobj(&A::thread_work, myobj, 15);
mytobj.join();
return 0;
}
|