线程传参及成员函数做线程函数

线程传参详解及成员函数做线程函数

1.传递临时对象作为线程参数

(1)要避免的陷阱1

  陷阱:当子线程函数参数使用指针时,实参与形参的地址相同。当使用detach()且主线程执行完毕,释放实参,此时形参会指向未知内存。

//myPrint1---参数为引用和指针---不推荐用,当detach时会出现问题。
void myPrint1(const int &i, char *pBuf)//待修改···
{	
	std::cout<

  修改:

//myhPrint2---相对安全
void myPrint2(const int i, const string &pBuf)//修改
{	
	std::cout<

(2)要避免的陷阱2

  陷阱:存在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()时,子线程也能安全执行。

(3)总结

  ① 若传递int,double等简单类型参数,建议都是值传递,不要使用引用;
  ② 如果传递string等类对象,避免隐式对象转换,而应该在创建子线程时,就显示构建临时对象。且在函数参数要用引用来接受,否则系统还会再构造一次临时对象出来,造成资源浪费。
  ③ 建议不适用detach(),只使用join(),这样就不存在局部变量失效导致线程对内存的非法引用问题。

(4)线程id的概念

  不同的线程,其线程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()<

  执行结果如下:
线程传参及成员函数做线程函数_第1张图片
  分析:可以看到,执行构造函数和拷贝构造函数的线程ID与主线程线程ID一致,也就意味着,当显示进行类类型转换时,会在主线程执行完毕之前,进行子线程的值传递。
  如果线程参数采用隐式转换,执行结果如下:
线程传参及成员函数做线程函数_第2张图片
  可以看到,构造函数的线程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()修饰后,形参和实参的地址相等,值也相等。
在这里插入图片描述
  另外,此时也不会再执行拷贝构造函数了(当然了,因为上面都说了是利用引用传参嘛)。

2.传递类对象,智能指针作为线程参数

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;
}

3.用成员函数指针做线程函数

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;
}

你可能感兴趣的:(C++并发与多线程)