C++ - 多线程 - 子线程调用的函数用引用传参

目录

1.不安全情况

2.安全的处理

3.const和ref之间有什么秘密联系?

4.网课链接


1.不安全情况

子线程用引用调用主线程中的变量是不安全的,尤其是在detach()的情况下,比如下面的例子:

class A {
public:
	int _m = 5;
	//可以把整形隐式转换成一个类对象
	A(int a) :_m(a) { cout << "调用构造函数   " <<  "类id: " << this_thread::get_id() << endl; }
	A(const A &a) :_m(a._m){ cout << "调用拷贝构造函数" << "拷贝类id: " << this_thread::get_id() << endl; }
	~A(){ cout << "调用析构函数" << endl; }
};
void myprint(const int &i ,const A &buf) {
	cout << "函数id: " << this_thread::get_id() << endl;
}

int main() {
	cout << "主id: " << this_thread::get_id() << endl;
	int mvar = 1;
	int mysecond = 12;
	thread my(myprint, mvar , mysecond);
	my.detach();

	return 0;
}

输出:

主id: 13788
调用构造函数

这种情况就是因为主线程已经结束了,回收了自己的变量后,子线程调用函数,并引用主线程的变量,当然是不行的,此时主线程变量已经没了。

2.安全的处理

就是在线程调用函数的时候,让主线程创建一个临时变量作为参数传递进去,这样就安全了,创建的方法就是在变量前加变量类型,如下:

thread my(myprint, int(mvar) , A(mysecond));

输出:

主id: 2920
调用构造函数   类id: 2920
调用拷贝构造函数拷贝类id: 2920
函数id: 调用析构函数析构类id: 2920

 可以看到该调用过程经历:主线程创建了一个临时变量->传递到函数后又由主线程将该临时变量拷贝了一份(因为这样情况下引用其实就变成值传递了)->子线程执行后续部分。

但此时主线程执行完了后,回收了变量,子线程中的拷贝变量怎么办呢?毕竟这些拷贝变量也是主线程拷贝的,会不会出问题呢?答案是不会,我们把detach改为join查看一下完整的打印结果看一下:

主id: 9116
调用构造函数   类id: 9116
调用拷贝构造函数拷贝类id: 9116
函数id: 12672
调用析构函数析构类id: 9116
调用析构函数析构类id: 12672

可以发现第一个析构其实是析构了主线程中的类,第二个析构是析构的拷贝类,其线程id是子线程的,所以说主线程虽然帮忙调用拷贝构造函数,这个对象的析构还是由子线程自己析构的。

detach没有完全打印并不是后面没执行,而是因为命令窗口是主线程的,主线程结束就不打印了即使子线程还在执行。

最好还是老老实实用join()吧!

3.const和ref之间有什么秘密联系?

我在听网课学习多线程时,偶然间发现了自己和老师的电脑跑的结果不一样,因此就引发了一个问题,这里整理一下,先看代码:

class A {
public:
	int _m = 5;
	A() { cout << "调用构造函数   " << "类id: " << this_thread::get_id() << endl; }
	A(const A &a) :_m(a._m){ cout << "调用拷贝构造函数" << "拷贝类id: " << this_thread::get_id() << endl; }
	~A(){ cout << "调用析构函数" << endl; }
};
void myprint(int &i ,A &buf) {
	cout << i++ << endl;    
	cout << buf._m++ << endl;      
	cout << "函数id: " << this_thread::get_id() << endl;
}

int main() {
	cout << "主id: " << this_thread::get_id() << endl;
	int mvar = 1;
	int mysecond = 12;
	A a;
    //thread my(myprint, ref(mvar) , ref(a)); 
	thread my(myprint, mvar , a);    //这样会报错,前面必须像上一行那样加ref()   
	cout << mvar << endl;     
	cout << a._m << endl;     
	my.join();

	return 0;
}

上述代码中,函数定义中参数是引用的形式,我在线程调用时前面必须加ref()才不报错,否则就会出现:

加了后输出:

主id: 2988
调用构造函数   类id: 2988
1
5
函数id: 15736
2
6
调用析构函数

但如果我在函数定义时给前面加上const,调用的时候就不用加ref()了!(加const同时也要把函数里修改变量的操作删掉)代码如下:

class A {
public:
	int _m = 5;
	A() { cout << "调用构造函数   " << "类id: " << this_thread::get_id() << endl; }
	A(const A &a) :_m(a._m){ cout << "调用拷贝构造函数" << "拷贝类id: " << this_thread::get_id() << endl; }
	~A(){ cout << "调用析构函数" << endl; }
};
void myprint(const int &i ,const A &buf) {
	/*cout << i++ << endl;
	cout << buf._m++ << endl;*/
	cout << "函数id: " << this_thread::get_id() << endl;
}

int main() {
	cout << "主id: " << this_thread::get_id() << endl;
	int mvar = 1;
	int mysecond = 12;
	A a;
	thread my(myprint, mvar , a);
	cout << mvar << endl;
	cout << a._m << endl;
	my.join();

	return 0;
}

此时输出:

主id: 13324
调用构造函数   类id: 13324
调用拷贝构造函数拷贝类id: 13324
函数id: 4504
1
5调用析构函数

调用析构函数

当然,此时加上也没错,不过输出结果变为:

主id: 14012
调用构造函数   类id: 14012
函数id: 88481

5
调用析构函数

也就是说加了ref就是正儿八经的引用,不加的话其实是用类似值传递的方法来形成了假的引用情况,但我们知道引用的话变量和他的引用的值应该是统一的,值传递形成的假引用为了满足这一条件,就用const限制用户:“如果你想用假引用,你就必须用const修饰别修改这个变量的值,否则值不统一用户就知道我是假引用啦!”

所以,如果想修改值,就老老实实用ref()引用或者变量前加mutable

ref(a) == &a

4.网课链接

c++11并发与多线程视频课程_哔哩哔哩_bilibili   的 P5

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
在Python中,可以使用thread模块或者threading模块来实现多线程编程。在多线程编程中,每个线程都有自己的代码执行流程和堆栈空间,但是它们共享进程的资源,如全局变量、文件、socket等。 如果要在多线程调用Python解释器,可以使用Py_NewInterpreter()函数来创建一个新的解释器对象,并在这个解释器对象中执行Python代码。 以下是一个示例代码: ``` import threading import ctypes import sys # 定义一个线程函数 def thread_func(): # 创建一个新的解释器对象 ctypes.pythonapi.Py_NewInterpreter() # 在新的解释器对象中执行Python代码 exec("print('Hello from new interpreter')") # 释放解释器对象 ctypes.pythonapi.Py_EndInterpreter() # 在线程中创建一个新的线程 t = threading.Thread(target=thread_func) t.start() # 在线程中执行Python代码 print('Hello from main interpreter') ``` 在上面的代码中,我们首先导入了threading模块和ctypes模块。然后定义了一个线程函数thread_func(),在这个函数中创建了一个新的解释器对象,并在这个解释器对象中执行了一段Python代码。最后,在线程中创建了一个新的线程,执行了一段Python代码,并输出了一些信息。 注意,在使用Py_NewInterpreter()函数创建一个新的解释器对象后,我们需要在新的解释器对象中执行一些Python代码,否则这个解释器对象会很快被释放。在上面的代码中,我们使用了exec()函数来执行一段简单的Python代码,你可以根据自己的需要来编写更复杂的代码。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值