C++11线程传递参数陷阱
一、传递引用以及指针
#include <iostream>
#include <thread>
void print(const int& var, const char* str)
{
std::cout << "print &var = " << &var << "\tstr = " << (void*)str << std::endl;
}
int main()
{
int var = 3;
const char str[] = "hello world!";
std::cout << "main &var = " << &var << "\tstr = " << (void*)str << std::endl;
std::thread mythread(print, var, str);
mythread.join();
return 0;
}
可以看到子线程和主线程中var的地址不同,str的地址相同,那var是用引用传递的为什么会出现地址不一样,是因为thread内部对var进行了复制而不是引用传递,需要引用传递需要使用std::ref来解决
这样主线程和子线程的var地址就相同了,但仍存在问题,如果将join换为detach,主线程和子线程分离,则会存在主线程执行完毕,局部变量空间呗操作系统回收,子线程在操作时,就会发生异常,所有要谨慎使用detach。
二、参数传递中存在隐式转换
#include <iostream>
#include <thread>
class A
{
public:
int temp;
A(int var) :temp(var)
{
std::cout << "构造函数执行" << std::endl;
std::cout << "执行的线程ID为" << std::this_thread::get_id() << std::endl << std::endl;
}
A(const A& Ta) :temp(Ta.temp)
{
std::cout << "拷贝构造函数执行" << std::endl;
std::cout << "执行的线程ID为" << std::this_thread::get_id() << std::endl << std::endl;
}
~A()
{
std::cout << "析构函数执行" << std::endl;
std::cout << "执行的线程ID为" << std::this_thread::get_id() << std::endl << std::endl;
}
};
void print(const int& var,const A& a)
{
std::cout << "子线程开始执行" << std::endl;
std::cout << "子线程ID为" << std::this_thread::get_id() << std::endl << std::endl;
}
int main()
{
int var = 3;
int a = 1;
std::cout << "主执行的线程ID为" << std::this_thread::get_id() << std::endl << std::endl;
std::thread mythread(print, std::ref(var), a);
mythread.join();
return 0;
}
可以观察到在参数传递中在子线程中对变量a进行了隐式转换为类A的对象,但是执行的构造的线程为子线程,如果不使用join使用detach可能存在子线程执行完毕,变量a已经被释放再在子线程进行构造会发生异常。如果需要解决这个问题,可以在主线程中构造一个临时对象。
可以看见创建临时对象后构造是在主线程之中完成,可以避免使用detach主线程结束,子线程还未结束发生异常的问题,但是可以发现又多了一次的拷贝构造函数,此时使用std::ref可以减少这次的拷贝构造。
三、参数传递智能指针
#include <iostream>
#include <thread>
void print(const std::unique_ptr<int> p)
{
std::cout << "子线程p = " << p << std::endl;
std::cout << "线程开始执行" << std::endl;
}
int main()
{
std::unique_ptr<int> p(new int(5));
std::cout << "主线程p = " << p << std::endl;
std::thread mythread(print,std::move(p));
std::cout << "主线程p = " << p << std::endl;
mythread.join();
return 0;
}
unique_ptr在作为参数时需要使用std::move进行传递,在传递参数后,主线程的p就是一个空指针了。