c++中多线程传递参数原理分析
线程可以共享进程的内存空间,线程拥有自己独立内存。
关于参数的传递,std::thread的构造函数只会单纯的复制传入的变量,特别需要注意的是传递引用时,传入的是值的副本,也就是说子线程中的修改影响不了主线程中的值。
值传递
主线程中的值,被拷贝一份传到了子线程中。
1 #include <iostream>2 #include <thread>3 4 using namespace std;5 6 void test(int ti, int tj)7 {8 cout << "子线程开始" << endl;9 //ti的内存地址0x0055f69c {4},tj的内存地址0x0055f6a0 {5} 10 cout << ti << " " << tj << endl; 11 cout << "子线程结束" << endl; 12 return; 13 } 14 15 16 int main() 17 { 18 cout << "主线程开始" << endl; 19 //i的内存地址0x001efdfc {4},j的内存地址0x001efdf0 {5} 20 int i = 4, j = 5; 21 thread t(test, i, j); 22 t.join(); 23 cout << "主线程结束!" << endl; 24 return 0; 25 }
传引用
从下面的运行结果,可以看出,即使是用引用来接收传的值,也是会将其拷贝一份到子线程的独立内存中,这一点与我们编写普通程序时不同。这是因为线程的创建属于函数式编程,所以为了传引用C++中才引入了std::ref()。关于std::ref()。
1 #include <iostream>2 #include <thread>3 4 using namespace std;5 6 class A{7 public:8 int ai;9 A (int i): ai(i) { } 10 }; 11 12 //这种情况必须在引用前加const,否则会出错。目前本人的觉得可能是因为临时对象具有常性 13 void test(const int &ti, const A &t) 14 { 15 cout << "子线程开始" << endl; 16 //ti的内存地址0x0126d2ec {4},t.ai的内存地址0x0126d2e8 {ai=5 } 17 cout << ti << " " << t.ai << endl; 18 cout << "子线程结束" << endl; 19 return; 20 } 21 22 23 int main() 24 { 25 cout << "主线程开始" << endl; 26 //i的内存地址0x010ff834 {4},a的内存地址0x010ff828 {ai=5 } 27 int i = 4; 28 A a = A(5); 29 thread t(test, i, a); 30 t.join(); 31 cout << "主线程结束!" << endl; 32 return 0; 33 }
那么如果我们真的需要像一般程序那样传递引用呢,即在子线程中的修改能够反映到主线程中。此时需要使用std::ref()。但是注意如果我们会在子线中改变它,此时用于接收ref()的那个参数前不能加const。关于C++多线程中的参数传引用问题,我目前只是记住了这个现象,关于原理还需后期研究源码继续学习。
1 #include <iostream>2 #include <thread>3 4 using namespace std;5 6 class A {7 public:8 int ai;9 A(int i) : ai(i) { } 10 }; 11 12 //接收ref()的那个参数前不能加const,因为我们会改变那个值 13 void test(int& ti, const A& t) 14 { 15 cout << "子线程开始" << endl; 16 cout << ti << " " << t.ai << endl; 17 ti++; 18 cout << "子线程结束" << endl; 19 return; 20 } 21 22 23 int main() 24 { 25 cout << "主线程开始" << endl; 26 int i = 4; 27 A a = A(5); 28 thread t(test, ref(i), a); 29 t.join(); 30 cout << "i改变:" << i << endl; 31 cout << "主线程结束!" << endl; 32 return 0; 33 }
传入类对象时,使用引用来接收比用值接收更高效。
1 #include <iostream>2 #include <thread>3 4 using namespace std;5 6 class A {7 public:8 int ai;9 A (int i) : ai(i) 10 { 11 cout << "构造" << this << endl; 12 } 13 14 A (const A& a) :ai(a.ai) { 15 cout << "拷贝构造" << this << endl; 16 } 17 18 ~A() 19 { 20 cout << "析构" << this << endl; 21 } 22 }; 23 24 //void test(const A a) 25 void test(const A& a) 26 { 27 cout << "子线程开始" << endl; 28 cout << "子线程结束" << endl; 29 return; 30 } 31 32 33 int main() 34 { 35 cout << "主线程开始" << endl; 36 int i = 4; 37 thread t(test, A(i)); 38 t.join(); 39 cout << "主线程结束!" << endl; 40 return 0; 41 }
传指针
从下面的运行结果,可以看出,主线程和子线程中的指针都是指向同一块内存。所以在这种情况下会有一个陷阱,如果使用detach(),则当主线程崩溃或者正常结束后,该块内存被回收,若此时子线程没有结束,那么子线程中指针的访问将未定义,程序会出错。
1 #include <iostream>2 #include <thread>3 4 using namespace std;5 6 7 void test(char *p) 8 {9 cout << "子线程开始" << endl; 10 //0x004ffeb4 "hello" 11 cout << p << endl; 12 cout << "子线程结束" << endl; 13 return; 14 } 15 16 17 int main() 18 { 19 cout << "主线程开始" << endl; 20 //0x004ffeb4 "hello" 21 char s[] = "hello"; 22 thread t(test, s); 23 t.join(); 24 cout << "主线程结束!" << endl; 25 return 0; 26 }
传临时对象
用临时变量作为实参时,会更高效,由于临时变量会隐式自动进行移动操作,这就减少了整体构造函数的调用次数。而一个命名变量的移动操作就需要std::move()。
1 #include <iostream>2 #include <thread>3 4 using namespace std;5 6 class A {7 public:8 int ai;9 A (int i) : ai(i) 10 { 11 cout << "构造" << this << endl; 12 } 13 14 A (const A& a) :ai(a.ai) { 15 cout << "拷贝构造" << this << endl; 16 } 17 18 ~A() 19 { 20 cout << "析构" << this << endl; 21 } 22 }; 23 24 void test(const A& a) 25 { 26 cout << "子线程开始" << endl; 27 cout << "子线程结束" << endl; 28 return; 29 } 30 31 32 int main() 33 { 34 cout << "主线程开始" << endl; 35 int i = 4; 36 thread t(test, A(i)); 37 t.join(); 38 cout << "主线程结束!" << endl; 39 return 0; 40 }
总结
1、使用引用和指针是要注意;
2、对于内置简单类型,建议传值;
3、对于类对象,建议使用引用来接收,以为使用引用会只会构造两次,而传值会构造三次;
4、在detach下要避免隐式转换,因为此时子线程可能还来不及转换主线程就结束了,应该在构造线程时,用参数构造一个临时对象传入。
c++中多线程传递参数原理分析相关推荐
- python默认参数举例_Python中的默认参数实例分析
本文研究的主要是Python中的默认参数的相关内容,具体如下. 熟悉C++语言的可以知道,C++语言中的默认参数是写在函数声明中的,为语法糖,与函数的调用无关,是在函数调用的时候由编译器补齐参数然后进 ...
- JDK 中的 BIO 实现原理分析(二十)
今天对JDK 中的 BIO 实现原理进行分析 一.简介 对比 Linux 上网络编程,我们会发现 JDK Socket 的编程逻辑是一模一样的.实际上也是这样,JDK 网络编程也没有做很多事,主要还是 ...
- python参数传递方法_深入理解python中函数传递参数是值传递还是引用传递
python 的 深入理解python中函数传递参数是值传递还是引用传递 目前网络上大部分博客的结论都是这样的: Python不允许程序员选择采用传值还是传 引用.Python参数传递采用的肯定是&q ...
- c语言va_start函数,va_start和va_end,以及c语言中的可变参数原理
FROM:http://www.cnblogs.com/hanyonglu/archive/2011/05/07/2039916.html 本文主要介绍va_start和va_end的使用及原理. 在 ...
- js中函数传递参数,究竟是值传递还是引用传递?
记住真理: js函数传递参数,不管是简单数据类型,还是引用数据类型,都是值传递!! 下面是js红包书里面的例子: function setName(obj) { obj.name = "Ni ...
- 转载】JQuery中如何传递参数如click(),change()等具体实现
转载地址:http://www.jb51.net/article/36249.htm 有个需求让两个select中option相互转换,这个作业就是给几个按钮添加click()事件接下来为大家介绍下如 ...
- Delphi 多线程传递参数的问题
unit uThread;interfaceusesClasses;typeTh = class(TThread)private{ Private declarations }protectedpro ...
- 数据结构与算法之KMP算法中Next数组代码原理分析
2019独角兽企业重金招聘Python工程师标准>>> 一.KMP算法之Next数组代码原理分析 1.Next数组定义 当模式匹配串T失配的时候,Next数组对应的元素指 ...
- Java-Java中的线程池原理分析及使用
文章目录 概述 线程池的优点 线程池的实现原理 线程池的使用 创建线程池 向线程池中提交任务 关闭线程池 合理的配置线程池 线程池的监控 概述 我们在上篇博文 Java-多线程框架Executor解读 ...
最新文章
- java静态/动态成员变量、初始化块,父类/子类构造函数执行顺序问题
- redis sentinel哨兵模式集群搭建教程
- 使用 FRP 反向代理实现 Windows 远程连接
- 信度和效度 智商计算
- Topaz Video Enhance AI中文版
- 低差别序列:高维序列(Halton sequence)
- Vue+axios配置踩坑记录
- 创建第一个Django项目
- kubernetes mysql ip_弄明白kubernetes中的“三种IP”
- mooc_java 集合框架中 学生所选课程2MapHashMap
- function函数嵌套 matlab_Matlab函数进阶:使用匿名函数和内嵌函数处理多变量传递问题...
- ruby map, reduce, select, reject, group_by
- php磁力链播放源码,Bt种子转磁力链 PHP源码
- oracle 文平,ORACLE调优之 内存结构调优(摘自文平书)
- 《功夫熊猫》的人生启示
- java开发高薪工程师,Java开发工程师如何获得高薪
- 2016/7/4日-你若安好,便是晴天.
- 如果我恨一个人,我就领他到中关村买相机。
- x^n mod 1003(快速求解法)
- 笔记本英雄联盟界面服务器停止运行,关于LOL选完大区(主界面)后马上闪退的正确解决方法...
热门文章
- JAVA复习(CharSequence接口、RunTime类、System类、object类中的finalize())
- android 恢复短信 失败,解决安卓手机发送短信失败的方法
- daterangepicker java_日期选择插件Date Range Picker
- php js 防止重复提交表单,php如何防止form重复提交
- php 蓝奏网盘上传文件,蓝奏云_文件上传_API
- 初中人教版电子课本app_电子课本|2020秋 部编人教版初中历史七年级上册教材电子课本(高清更新可打印)...
- 这个陶瓷电阻烙铁架不错哦,最新一期的电子趣事分享给大家
- HDLBits答案(19)_Verilog有限状态机(6)
- html标签强制转换位置,王老师html零基础学习笔记第4课——样式初始化+类型转化...
- linux ftp 553,修复使用vsftp出错553 Could not create file的有效方法