5分钟速成C++14多线程编程
原文链接:Learn C++ Multi-Threading in 5 Minutes
C++14的新的多线程架构非常简单易学,如果你对C或者C++很熟悉,那么本文非常适合你。作者用C++14作为基准参考,但是所介绍的东西在C++17中也依然适用。本文只介绍基本的架构,在读完本文后你应该也可以自己编写自己的多线程程序。
创建线程
创建线程有以下几种方式:
1.使用函数指针
2.使用仿函数
3.使用lambda表达式
这些方式都比较类似,只有部分差别,我将在下面具体讲述每一种方式和他们的区别。
使用函数指针
来看看下面这个函数,其参数包括一个vector的引用 v ,一个输出结果的引用acm,还有两个v的索引。这个函数会将v的beginIndex和endIndex之间的元素累加起来。
1 void accumulator_function2(const std::vector<int> &v, unsigned long long &acm, 2 unsigned int beginIndex, unsigned int endIndex) 3 { 4 acm = 0; 5 for (unsigned int i = beginIndex; i < endIndex; ++i) 6 { 7 acm += v[i]; 8 } 9 }
现在如果我们想将vector分为两个部分,并在单独的线程t1和t2中分别计算各部分的总和的话,我们可以这么写:
1 //Pointer to function 2 { 3 unsigned long long acm1 = 0; 4 unsigned long long acm2 = 0; 5 std::thread t1(accumulator_function2, std::ref(v), 6 std::ref(acm1), 0, v.size() / 2); 7 std::thread t2(accumulator_function2, std::ref(v), 8 std::ref(acm2), v.size() / 2, v.size()); 9 t1.join(); 10 t2.join(); 11 12 std::cout << "acm1: " << acm1 << endl; 13 std::cout << "acm2: " << acm2 << endl; 14 std::cout << "acm1 + acm2: " << acm1 + acm2 << endl; 15 }
上面这段你需要知道:
1.std::thread这个调用创建了一个新的线程。其第一个参数是函数指针 accumulator_function2 ,因此每个线程都会去执行这个函数。
2.剩下的我们传给std::thread的构造函数的参数,都是我们需要去传给accumulator_function2的参数。
3.重点:传递给accumulator_function2的参数默认情况下都是值传递的,除非你用std::ref把他包起来。所以我们这里使用了std::ref来包住v、acm1、acm2。
4.使用std::thread创建的线程是没有返回值的,所以如果你想从线程中返回些什么,请使用引用将你想返回的值作为一个传入参数。这里的例子就是acm1和acm2。
5.每个线程一旦创建就立即执行了。
6.我们使用join()函数来等待线程执行完毕。
使用伪函数
你也可以使用伪函数来做同样的事情,下面是例子:
1 class CAccumulatorFunctor3 2 { 3 public: 4 void operator()(const std::vector<int> &v, 5 unsigned int beginIndex, unsigned int endIndex) 6 { 7 _acm = 0; 8 for (unsigned int i = beginIndex; i < endIndex; ++i) 9 { 10 _acm += v[i]; 11 } 12 } 13 unsigned long long _acm; 14 };
那么创建线程的方式变成下面这样:
1 //Creating Thread using Functor 2 { 3 4 CAccumulatorFunctor3 accumulator1 = CAccumulatorFunctor3(); 5 CAccumulatorFunctor3 accumulator2 = CAccumulatorFunctor3(); 6 std::thread t1(std::ref(accumulator1), 7 std::ref(v), 0, v.size() / 2); 8 std::thread t2(std::ref(accumulator2), 9 std::ref(v), v.size() / 2, v.size()); 10 t1.join(); 11 t2.join(); 12 13 std::cout << "acm1: " << accumulator1._acm << endl; 14 std::cout << "acm2: " << accumulator2._acm << endl; 15 std::cout << "accumulator1._acm + accumulator2._acm : " << 16 accumulator1._acm + accumulator2._acm << endl; 17 }
上面这段你需要知道:
伪函数的使用方式大部分地方都和函数指针很像,除了:
1.第一个参数变成了伪函数对象。
2.我们不再需要使用引用来获取返回值了,我们可以将返回值作为伪函数对象的一个成员变量来储存。这里的例子就是_acm。
使用lambda表达式
作为第三种选择,我们可以在每个线程的构造函数中使用lambda表达式来定义我们想做的事,如下:
1 { 2 unsigned long long acm1 = 0; 3 unsigned long long acm2 = 0; 4 std::thread t1([&acm1, &v] { 5 for (unsigned int i = 0; i < v.size() / 2; ++i) 6 { 7 acm1 += v[i]; 8 } 9 }); 10 std::thread t2([&acm2, &v] { 11 for (unsigned int i = v.size() / 2; i < v.size(); ++i) 12 { 13 acm2 += v[i]; 14 } 15 }); 16 t1.join(); 17 t2.join(); 18 19 std::cout << "acm1: " << acm1 << endl; 20 std::cout << "acm2: " << acm2 << endl; 21 std::cout << "acm1 + acm2: " << acm1 + acm2 << endl; 22 }
同样,大多数地方都和函数指针的方式很类似,除了:
1.作为传参的替代方式,我们可以使用lambda表达式的捕获(capture)方式来处理参数传递
Tasks, Futures, and Promises
除了std::thread,我们还可以使用 tasks.
tasks和std::thread工作的方式非常相似,只有一个最主要的不同:tasks可以返回一个值。因此,你可以暂存这个返回值来作为这个线程的更抽象的定义方式,并在你真的需要返回的结果的时候来从这个返回值中拿到数据。
下面就是使用Tasks的例子:
1 #include <future> 2 //Tasks, Future, and Promises 3 { 4 auto f1 = [](std::vector<int> &v, 5 unsigned int left, unsigned int right) { 6 unsigned long long acm = 0; 7 for (unsigned int i = left; i < right; ++i) 8 { 9 acm += v[i]; 10 } 11 12 return acm; 13 }; 14 15 auto t1 = std::async(f1, std::ref(v), 16 0, v.size() / 2); 17 auto t2 = std::async(f1, std::ref(v), 18 v.size() / 2, v.size()); 19 20 //You can do other things here! 21 unsigned long long acm1 = t1.get(); 22 unsigned long long acm2 = t2.get(); 23 24 std::cout << "acm1: " << acm1 << endl; 25 std::cout << "acm2: " << acm2 << endl; 26 std::cout << "acm1 + acm2: " << acm1 + acm2 << endl; 27 }
上面这段你需要知道:
1.tasks使用std::async创建
2.std::async的返回值是一个叫std::future的类型。别被他的名字唬到,他的意思是t1和t2的值会在未来被真正的赋值。我们通过调用t1.get()来获得他的真正的返回值。
3.如果future的返回值还没有准备好(任务还没有计算完成),那么调用get()的主线程会被卡住,直到准备好了返回值(和join()的行为一样)。
4.注意,我们传递给std::async的函数(实际上是lambda表达式)是有返回值的,这个返回值用过一个叫做std::promise的类型来传递。大多数情况下你不需要了解任何promise的细节,C++在幕后可以处理好这些事情。
5.默认的情况下,tasks也会在创建之后立刻运行(有办法来修改这个行为,但是本文没有涉及)。
线程创建的总结:
创建线程很简单,你可以通过函数指针、伪函数、lambda表达式的方式来创建std::thread,也可以使用std::async的方式来获得一个std::future类型的返回值。std::async也同样可以使用函数指针、伪函数、lambda表达式来创建
(未完待续)
转载于:https://www.cnblogs.com/winnersun/p/10591646.html
5分钟速成C++14多线程编程相关推荐
- java 四舍五入_《JAVA编程思想》5分钟速成:1-4章:概述
前言: 1.面向对象的特征有哪些方面? 2.Math.round(11.5) 等于多少? Math.round(-11.5)等于多少? 3.float f=3.4;是否正确? 4.short s1 = ...
- c++ 模板类实现堆栈实验报告_5分钟学会C/C++多线程编程进程和线程
前言 对线程有基本的理解 简单的C++面向过程编程能力 创造一个简单的线程. 创造单个带参数的线程. 如何等待线程结束. 创造多个线程,并使用互斥量来防止资源抢占. 会使用之后,直接跳到"汇 ...
- java 析构函数_《JAVA编程思想》5分钟速成:第5章(初始化和清理)
第五章.初始化和清理 前言 1.初始化顺序(静态成员.非静态成员,构造器.父类构造器)的排序: 2.构造器(constructor)是否可被重写(override)? 3.final, finally ...
- Siege(开源Web压力测试工具)——多线程编程最佳实例
在英语中,"Siege"意为围攻.包围.同时Siege也是一款使用纯C语言编写的开源WEB压测工具,适合在GNU/Linux上运行,并且具有较强的可移植性.之所以说它是多线程编程的 ...
- Siege——多线程编程最佳实例
在英语中,"Siege"意为围攻.包围.同时Siege也是一款使用纯C语言编写的开源WEB压测工具,适合在GNU/Linux上运行,并且具有较强的可移植性.之所以说它是多线程编程的 ...
- Delphi多线程编程基础入门
1. 概述 对于开发人员来说,多线程是必备的知识,但相对来说,也是比较难的知识点.Delphi是一门古老而优秀的编程语言,它对多线程的处理有一些特殊的地方,本文尝试做一些简单的讲解,可以当作Delph ...
- Siege多线程编程最佳实例微信约战斗牛网站搭建
编程最佳实例微信约战斗牛网站搭建论坛:aqiulian.com,更多编程实例咨询Q:212303635.在英语中,"Siege"意为围攻.包围.同时Siege也是一款使用纯C语言编 ...
- 多线程编程技术开发资料
多线程编程技术开发资料 目录 Win32 多线程的性能(1)... 1 Win32 多线程的性能(2)... 10 关于多线程的一些细节... 23 用VC++5.0 实 现 多 线 程 的 调 度 ...
- 常见的面试算法题:创建几个线程按顺序打印数字或者字母(多线程编程)(套用该模板即可)
常见的面试算法题:创建几个线程按顺序打印数字或者字母(多线程编程)(套用该模板即可) 比较典型的题目,如下有: 题目一: 1.启动3个线程打印递增的数字, 线程1先打印1,2,3,4,5, 然后是线程 ...
最新文章
- 1行代码消除PyTorch的CUDA内存溢出报错,这个GitHub项目刚发布就揽星600+
- 远程办公时,有哪些提高沟通效率的技巧?
- 用python连接redis时错误InvalidResponse: Protocol Erro...
- 张队长主讲这堂 .NET Core技术培训公开课,太原你约不约
- bash的一些小技巧
- python3人工智能网盘_《Python3入门人工智能掌握机器学习+深度学习提升实战能力》百度云网盘资源分享下载[MP4/5.77GB]...
- 安卓自定义控件的原理
- springboot+springcloudgateway+nacos+sleuth+zipkin+mysql
- jna调取第三方dll实战(罗技G29)
- 数学表达式: 从恐惧到单挑 (1. 概述)
- vf程序设计与c语言,全国计算机等级考试vf和C语言哪个更好
- NAS与文件系统服务器比较,磁盘阵列与NAS服务器的比较
- linux无法识别sas硬盘,关于SNMP监控SAS硬盘问题?
- safari java 插件_精通Safari – 如何在 Mac 版 Safari 中使用互联网插件
- Matlab/simulink 风电风机一次调频,变桨控制,变风速调频对比,转子动能控制,虚拟惯性控制,风机内部控制详细,频域模型,DFIG,PMSG
- 世界顶级思维(必须收藏)
- SAP的SLED的2个验证
- STL和FIG文件的结构
- 官宣丨全球边缘计算大会参会指南出来啦!
- 百度正式推出外链工具beta版本
热门文章
- 【zookeeper系列】centos7安装zookeeper
- php nodelist,了解NodeList、HTMLCollection以及NamedNodeMap的使用(代码)
- java接收前台tex格式t数据_java 下载文件时,设置response.setContentType 根据文件类型...
- 根目录_Ubuntu的根目录下的var/log/apt突然爆满,电脑卡死
- Xcode插件管理工具Alcatraz
- linux学习笔记1:基础知识
- RSA2013系列(1):中国成为热点
- CCNA试验-NAT
- 数据绑定,vs2005
- spark代码连接hive_spark连接Hive