C++并发与多线程(二) 传递临时对象做线程参数

传递临时对象做线程参数

问题一

void myPrint(const int &i, char *buf) {
	cout << i << endl;
	cout << buf << endl;
}

int main() {
	
	int i = 1;
	char mybuf[] = "this is a test!";
	//创建线程
	thread mythread(myPrint, i, mybuf); //线程的函数传参直接顺序写到后面即可

	mythread.join();

	cout << "主线程执行结束" << endl;


}

线程参数的传递方法如上:

thread mythread(myPrint, i, buf);需要传递的线程参数,直接按照顺序写到thread创建线程的后面即可。

如果是thread.join(),主线程等待子线程执行完毕,传参是没有什么大坑的,但是一旦涉及到thread.detach()之后,就要注意传参,因为主线程和子线程分离,主线程执行完毕就会释放资源,诸如i = 1;char buf[] = "this is a test!";等局部变量都会被释放,那么子进程的访问时候会受到影响?

int main() {
	
	int i = 1;
	char mybuf[] = "this is a test!";
	//创建线程
	thread mythread(myPrint, i, mybuf); //线程的函数传参直接顺序写到后面即可

	//mythread.join();

	mythread.detach(); //使用detach()替换join()

	cout << "主线程执行结束" << endl;


}

在这里插入图片描述
结论如下:

  1. 非指针类传参,不会受影响,因为thread创建是拷贝构造,所有局部变量都会被创建一份新的拷贝,主线程释放资源不会影响到子线程的访问。
  2. 指针类传参,会受影响。因为默认的拷贝构造对于thread是浅拷贝,指针类型直接指向原来的地址,没有开辟新的空间。

解决办法:

  1. 使用类的仿函数做线程参数,重写拷贝构造函数为深拷贝。
  2. char*改为string

代码示例如下:

  1. 重写深拷贝
class MyPrint {
public:
	//有参构造
	MyPrint(int i, char *buf) {
		m_i = i;
		m_buf = new char*(buf);
	}
	//拷贝构造函数
	MyPrint(const MyPrint &p) {
		m_i = p.m_i;
		m_buf = new char*(*p.m_buf);
	}
	//析构函数
	~MyPrint() {
		//对申请的堆进行释放
		if (m_buf != NULL) {
			delete m_buf;
			m_buf = NULL;
		}
	}

	void operator()(){
		cout << m_i << endl;
		cout << *m_buf << endl;
	}

	int m_i;
	char **m_buf; //注意这里必须用二级指针
};


int main() {
	
	int i = 1;
	char mybuf[] = "this is a test!";
	MyPrint p(i, mybuf);
	//创建线程
	thread mythread(p); //线程的函数传参直接顺序写到后面即可

	/*mythread.join();*/

	mythread.detach();

	cout << "主线程执行结束" << endl;

	return 0;
}
  1. 用string代替char
void myPrint(const int &i, const string &buf) { //这里必须用const string *
	cout << i << endl;
	cout << buf << endl;
}

问题二

对于问题一的解决方案二,使用string替换char *,会带来一个潜藏bug:main中的mybuf(char *)究竟是什么时候转为buf(string)传入子线程的?

事实上,这个时机是不确定的(多线程带来的不确定因素)存在一种可能,主线程main执行完毕,mybuf被回收后,系统才去转string,这就打印错了内容!

同样的对于问题一的解决方案一,也会遇到这个问题,对象的拷贝构造可能会在main结束之前发生,这样main中的mybuf已经被释放,就导致打印出乱码。
在这里插入图片描述

解决方法:

在main中,创建一个临时的string,直接先把mybuf转string

void myPrint(const int &i, const string &buf) { //用string代替cahr *
	cout << i << endl;
	cout << buf << endl;
}
int main() {
	
	int i = 1;
	char mybuf[] = "this is a test!";
	//创建线程
	thread mythread(myPrint, i, string(mybuf)); //必须用string转换一下
	/*mythread.join();*/
	mythread.detach();
	cout << "主线程执行结束" << endl;

	return 0;
}

总之:在创建子线程之前,我们必须强制他构造出我们想要的类型,而不是等他隐式构造(mybuf(char *)转buf(string)),所以要用一个string(mybuf)来传参。

小结:

  1. 如果传递int这种简单类型,推荐使用值传递,不要用引用
  2. 如果传递类对象,避免使用隐式类型转换,全部都是创建线程这一行就创建出临时对象,然后在函数参数里,用引用来接,否则还会创建出一个对象
  3. 终极结论:建议不使用detach(),这样就不会有局部变量失效导致非法引用的问题,这都是因为detach()导致的父子线程分开运行。

线程id

  1. id是个数字,每个线程(不管是主线程还是子线程)实际上都对应着一个数字,而且每个线程对应的这个数字都不一样
  2. 线程id可以用C++标准库里的函数来获取。std::this_thread::get_id();来获取

thread线程传参理解参考文章:

std::thread线程类及传参问题

C++的std::thread是怎么进行参数传递的?

std::ref

std::reference_wrapper用于按引用传递对象给 std::bindstd::thread 的构造函数.

std::ref()和&

智能指针

#include <iostream>
#include <thread>
#include <memory>
using namespace std;

void myPrint(unique_ptr<int> ptn)
{
	cout << "thread = " << std::this_thread::get_id() << endl;
}

int main()
{
	unique_ptr<int> up(new int(10));
	thread mythread(myPrint, std::move(up));
	mythread.join();
	//mythread.detach();

	return 0;
}
  1. 独占式指针只能通过std::move()才可以传递给另一个指针
  2. 传递后up就指向空,新的ptn指向原来的内存
  3. 所以这时就不能用detach了,因为如果主线程先执行完,ptn指向的对象就被释放了

C++智能指针简单剖析

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值