主线程是如何向子线程传递数据的?_c++ 利用thread创建线程

用进行多线程开发

小时候,老师总是教育我们上课要专心,“一心不可二用”。可是CPU这个不听话的“熊孩子”偏偏却在一个芯片中加入了两个甚至多个运算核心,想要一“芯”二用。从硬件厂商的角度,通过增加CPU的运算核心,突破了原来单核CPU的频率极限,确实可以很大程度上增加CPU的总频率。在他们看来,这简直就是一个天才的创意。可是从软件厂商的角度,CPU运算核心的增加,并没有显著地提高软件的性能表现,有时候甚至会降低软件的性能。在他们看来,这无疑是一场噩梦的开始。

在以往的计算机发展历史中,硬件技术的发展,特别是CPU频率的不断提升,总是同时会提升软件的性能。更重要的是,这种性能的提升是完全免费的,软件什么都不需要做,只要换上新出来的更高频率的CPU,软件就获得更好的性能表现。从286到486,从奔腾到酷睿,每次CPU频率的提升,无不给软件性能带来大幅提升。如果有用户抱怨我们的软件性能不佳,我们也无须着急,只需要坐等Intel或者AMD推出更高频率的CPU并让用户换上就可以了。

但是,当CPU的发展进入多核时代以后,程序员们沮丧地发现,CPU长期提供的这份“免费的午餐”消失了。这是因为当前大多数程序几乎都是针对一个运算核心的CPU而设计的单线程程序。虽然CPU有多个运算核心,但它却只能在其中的某一个运算核心上进行运算,而其他的运算核心并没有得到利用而白白浪费了。虽然CPU的运算核心增加了,总的频率增加了,但是单个运算核心的频率并没有太大变化,所以程序的性能并没有随着CPU运算核心的增加而得到显著的提升。

fcd1c7091523b110828bea07b9518a99.png

从单核CPU到多核CPU

浪费是可耻的,更何况我们浪费的是如此宝贵的CPU运算资源。为了把被浪费的CPU运算资源充分地利用起来,唯一的办法就是针对CPU的多个运算核心设计我们的程序,通过将原来由单个线程串行执行的程序并行化,用多线程代替原来的单线程,使其可以同时运行在多个运算核心上,以此来实现对CPU多个运算核心的充分利用。这样,程序的性能又会随着CPU频率的提高而得到提升。

可是,多线程程序的设计并不是一个轻松简单的活儿。在C++11之前,如果我们想要实现一个多线程程序,我们需要使用系统API创建线程,需要小心地维护对共享资源的访问等等。更折磨人的是,多线程增加了程序的设计与实现难度,如果设计错误,多线程不仅不会提升程序的性能,反而可能会降低性能,甚至引起资源互锁而导致程序失去响应。为了简化多线程程序的设计与实现,C++11的标准库专门提供了头文件以支持多线程程序的开发,而那份美味的“免费的午餐”也正在回到我们面前。

利用thread创建线程

C++11中的头文件提供了thread、mutex以及unique_lock等基本对象来对多线程开发中最常用的线程、互斥以及锁等基本概念进行抽象与表达,为多线程程序的实现提供了一个较低抽象层次的编程模型。其中,最基础也最重要的是与线程概念相对应的thread类。线程是对程序中的某个执行或者计算过程的一种表述,而所谓的多线程程序,就是将原来的一个执行过程分成多个过程去执行。由此可见,线程的创建,是实现多线程的基础。可是在以往,要想在程序中创建一个线程,我们不得不针对不同的操作系统调用不同的系统函数,然后还要提供复杂的参数才能完成线程的创建。幸运的是,thread类的出现,大大地简化了这一过程。

thread类对线程概念进行了很好的抽象与实现,从而使得我们可以非常简单地使用一个函数指针(也包括函数对象和Lambda表达式)来构建一个thread对象。而一旦拥有了thread对象,就意味着我们创建了一个线程,也就可以利用thread对象所提供的成员函数对这个线程进行调度,启动、挂起或者停止这个线程,以操作线程完成某个执行过程。例如:

#include // 引入定义thread类的头文件#include  // 使用thread所在的名字空间using namespace std; // 定义需要线程执行的函数和函数对象void ListenMusic(){cout<

在这段代码中,我们利用函数对象类ReadBook的一个read函数对象和指向ListenMusic()函数的函数指针(也就是它的函数名)作为thread类构造函数的参数,分别创建了readthread和listenthread这两个thread对象。thread对象的创建,意味着它将创建新的线程,并开始执行作为构造函数参数传递给thread对象的函数对象或者是函数,通过简单的一个步骤,就完成了线程的创建与启动执行。在多线程环境下,我们将执行主函数并负责创建其它线程的线程称为主线程,而那些被创建的线程则相应地被称为分支线程或工作者线程,其执行的函数则被称为线程函数。在执行的时候,主线程开始进入主函数执行,通过创建两个thread对象而创建了两个分支线程并立即启动执行其线程函数,而与此同时,执行主函数的主线程将继续向下执行。这样,主线程和两个分支线程同时都在执行,操作系统会将它们调度到CPU的多个运算核心去执行,以此达到对CPU多个运算核心的充分利用。当主线程遇到thread对象调用的join()函数后,主线程将等待这个thread对象执行完毕之后,再继续往下执行,直到最后主函数执行完毕,退出整个程序。整个程序的执行流程如下图12-4所示:

9beb19efbe095667ee346bcb82bacd2c.png

多线程程序的执行流程

除了利用thread对象创建新的线程简单地执行某个线程函数之外,就像我们常常需要通过参数与普通函数进行数据传递一样,更多时候,我们也需要向线程函数内传入数据以供其进行处理,或者是从线程函数中传出结果数据。要做到这一点,我们同样需要给线程函数加上参数,跟普通函数类似,如果只是需要向线程函数内传入数据,那就加上传值形式的参数,而如果加上传指针和传引用形式的参数,则既可以传入也可以传出数据。与普通函数在调用时将实际参数复制给函数的形式参数所不同的是,线程函数的形式参数的赋值是在这个函数被用于创建thread对象时完成的。当我们在使用某个带有参数的线程函数创建thread对象时,在thread构造函数的实际参数中,我们不仅要用这个函数指针或者函数对象做第一个参数,同时其后还要依次加上线程函数所需要的各个实际参数。在创建thread对象的时候,这些实际参数会被拷贝复制给线程函数相应的形式参数,以此来实现数据的传递。这里需要注意的是,如果线程函数的参数是传引用形式,那么在创建thread对象的时候,我们需要使用ref()函数获得实际参数的引用才行,否则,即使这个参数是引用形式,它也会被拷贝复制而在线程函数和本地函数间形成两个副本,起不到传出数据的效果。例如,我们需要向上面的ListenMusic()线程函数传入歌曲名并从中传出结果数据:

// 需要传递数据的线程函数// 传值形式的strSong负责向线程函数内传入数据// 传引用形式的vecEar负责向线程函数外传出数据void ListenMusic(string strSong,vector& vecEar){ cout< vecEar; // 用于保存结果数据的容器 string strSong = "歌唱祖国"; // 传入线程函数的数据 // 在创建thread对象时传递数据// 第一个参数是线程函数指针,其后依次是线程函数所需要的参数thread listenthread(ListenMusic,strSong,ref(vecEar)); // …listenthread.join();  // 输出结果数据 for(string strName : vecEar) cout<

利用thread对象,我们可以像调用普通函数一样简单地用thread对象创建另外一个线程来执行我们的线程函数,也可以像与普通函数传递数据一样简单地与线程函数传递数据,从此,一边听着歌还可以一边看着书,轻松开启我们惬意的“一芯二用”的并行生活。

知道更多:线程中的瞌睡虫

在利用线程执行某个任务的时候,我们往往要对线程的执行时间进行控制,让线程在等待一定时间之后再继续执行,或者是在某个事先设定的固定时间点之后执行。这时,我们就需要用到std::this_thread名字空间下的sleep_for()函数和sleep_until()函数来完成对线程执行状态的时间控制了。

sleep_for()函数可以让当前线程(也就是调用这个函数的线程)暂停执行一段时间,等过了这段时间之后再继续恢复执行;而sleep_until()函数则是让当前线程一直暂停,直到某个固定时间点的到来才会继续恢复执行。它们就像两条瞌睡虫,一条可以让线程瞌睡一整天(固定时间段),而另一条更厉害,可以让线程一直瞌睡到天明(固定时间点)。对于我们来说,瞌睡虫很是讨厌,可是对于线程来说,瞌睡虫却是大有用处。

比如,我们想要模拟一下传说中的2012世界末日,就需要这两条瞌睡虫来让线程瞌睡瞌睡:

#include #include  // 引入线程相关的头文件#include  // 引入时间相关的头文件 using namespace std;using namespace std::chrono; // 使用时间相关的名字空间 int main(){// 构造一个固定时间点:2012年 12月21日零时 tm timeinfo = tm(); timeinfo.tm_year = 112; // 年: 2012 = 1900 + 112timeinfo.tm_mon = 11; // 月:12 = 1 + 11 timeinfo.tm_mday = 21; // 21日time_t tt = mktime(&timeinfo);// 利用time_t类型的变量tt创建一个表示世界末日固定时间点的time_point对象tp system_clock::time_point tp = system_clock::from_time_t (tt); // 当前线程一直瞌睡到tp表示的2012年12月21日零时this_thread::sleep_until(tp);  // 世界末日到了,程序继续恢复执行,响铃10次发出警报for(int i = 0; i < 10; ++i){cout<

在两条瞌睡虫的合作下,我们的模拟程序会首先在sleep_until()的作用下,瞌睡(暂停执行)到tp所表示的固定时间点(2012年12月21日零时),等到了这个时间点后,程序才会恢复执行。紧接着,程序执行进入一个for循环,每次循环,它都会输出一个计算机响铃,然后又会在sleep_for()这条瞌睡虫的作用下,休眠一秒钟,然后再继续执行下一次循环。整个程序的效果就是到了世界末日,这个程序会发出滴滴滴的警报,告诉我们,世界末日来了,赶紧逃命吧!可是,可是,2012早都过去了,虽然世界末日没来,可这个程序却忠实地准时发出了警报。由此可见,玛雅人忽悠人,还是C++程序更可信。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/543154.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

php多维数组交集,求数组差/交集函数-php数组函数(二)

求数组差集函数函数只检查了多维数组中的一维。可以用 array_diff($array1[0], $array2[0]) 检查更深的维度。u&#xff1a;自定义函数比较&#xff0c;a(association)&#xff1a;同时比较键和值。自定义函数callable $value_compare_func必须返回一个小于零&#xff0c;等于零…

正则过滤符号_多角度理解正则项

过拟合和欠拟合什么是过拟合和欠拟合过拟合&#xff1a;模型在训练集上效果好&#xff0c;在测试集上效果差欠拟合&#xff1a;在训练集上效果就不好产生过拟合的原因参数太多&#xff0c;模型复杂度太高数据量少&#xff0c;训练轮次过多样本中噪声较大&#xff0c;模型拟合了…

PHP页面中嵌套go语言,go语言嵌套类型的使用细节

1. 定义在Go语言中&#xff0c;嵌套类型是将已有的类型直接声明在新的结构类型里。被嵌入的类型被称为新的外部类型的内部类型。通过嵌入类型&#xff0c;与内部类型相关的成员变量会提升到外部类型上。就好像这些成员变量直接声明在外部类型一样。如下图所示&#xff1a;外部类…

jvm内存模型_JVM内存模型的相关概念

1.前言Android的虚拟机是根据移动设备的特点基于Java虚拟机(JVM)改进而来&#xff0c;虽然没有保留规范&#xff0c;但作为Java语言的使用者&#xff0c;了解一下JVM的规范还是有必要的。2.JVM内存模型JVM在执行Java程序时&#xff0c;会把它管理的内存划分为若干个的区域&…

LNMP/LEMP(PHP7.0.04+mysql5.7.12+nginx1.10.0)

预览:Installing Nginx with PHP 7 and MySQL 5.7 (LEMP) on Ubuntu 16.04 LTSThis tutorial exists for these OS versionsUbuntu 15.10 (Wily Werewolf)Ubuntu 14.04 LTS (Trusty Tahr)Ubuntu 13.04 (Raring Ringtail)Ubuntu 12.10 (Quantal Quetzal)Ubuntu 12.04 LTS (Preci…

length函数的头文件_Framebuffer 应用编程中涉及的 API 函数

本节程序的目的是&#xff1a;打开 LCD 设备节点&#xff0c;获取分辨率等参数&#xff0c;映射 Framebuffer&#xff0c;最后实现描点函数。5.2.1 open 函数在 Ubuntu 中执行“man 2 open”&#xff0c;可以看到 open 函数的说明&#xff1a;头文件&#xff1a;#include <s…

xib中UIScrollView固定底部内容

UIScrollView的高度在xib中设置 需要固定的部分用代码添加: UIImageView *textLogo [[UIImageView alloc] initWithFrame:CGRectMake((kMSScreenWith - 153)/2, (kMSScreenHeight - 118 > 549 ? kMSScreenHeight : 667) - 173, 153, 35)]; textLogo.image [UIImage image…

hive

hive 的 表与hdfs数据关系映射放在元数据库中&#xff0c;也就是mysql中&#xff0c;而真正的数据放在 hdfs中&#xff0c;通过mysql中表 &#xff0c;字段等与hdfs上数据的映射来查询 1.hive是基于Hadoop的一个数据仓库工具&#xff0c;可以将结构化的数据文件映射为一张数据库…

判断图像局部过暗_CVPR 2020丨基于记忆增强的全局局部整合网络:更准确的视频物体检测方法...

编者按&#xff1a;在视频物体检测任务中&#xff0c;由于相机失焦、物体遮挡等问题&#xff0c;仅基于图像的目标检测器很可能达不到令人满意的效果。针对此类问题&#xff0c;微软亚洲研究院提出了基于记忆增强的全局-局部整合网络(Memory Enhanced Global-Local Aggregation…

第三章:多态

一、多态 1、什么是多态? 解析&#xff1a;不同的对象对于同一个操作&#xff0c;做出的响应不同 具有表现多种形态的能力的特征 2、使用多态的优点 解析&#xff1a;为了实现统一调用 一个小例子&#xff1a;<父类类型作为参数> 父类(Pet) 子类(Gog,Penguin) 主人类(Ma…

请检查virtualboxapi是否正确安装_电机行业安装绝缘轴承规范

1.安装前检查轴承安装前&#xff0c;请检查滚动体表面是否有毛刺&#xff0c;划痕或裂缝。 轴承的径向游隙和轴向游隙是否合格&#xff0c;通常仅测量径向游隙。 其次&#xff0c;检查轴承型号是否正确。 有关滚动轴承的径向游隙标准&#xff0c;请参见下表。 例如&#xff1a;…

matlab时域离散信号与系统,时域离散信号和系统的频域分析

信号与系统的分析方法有两种&#xff1a;时域分析方法和频域分析方法。在连续时间信号与系统中&#xff0c;信号一般用连续变量时间t的函数表示&#xff0c;系统用微分方程描述&#xff0c;其频域分析方法是拉普拉斯变换和傅立叶变换。在时域离散信号与系统中&#xff0c;信号用…

genymotion 此应用与您的手机不兼容

2019独角兽企业重金招聘Python工程师标准>>> 安装arm芯片的转换包&#xff0c;转换包的下载地址&#xff1a;http://pan.baidu.com/s/1dE2EqAH 只要把下载下来的压缩包拖动到模拟器中&#xff0c;点击确定后安装。 安装完成后&#xff0c;需要重启模拟器。 转载于:h…

安卓按钮设置背景颜色不管用_MIUI10新功能:时钟背景黑白自选、公交卡自定义时段唤起...

经历清明假与米粉节后&#xff0c;MIUI10恢复开挂模式&#xff0c;开发版每周给大家推送新功能。本期主要跟大家分享两个要点&#xff0c;其一是时钟背景颜色支持用户选择黑色或者白色&#xff0c;其二是NFC刷卡支持设置优先唤醒时间&#xff0c;降低手工切换操作。■时钟背景可…

数字电视 星座图 matlab,数字电视 MER 及星座图剖析

向天明中国电子测量与仪器学会委员随着数字电视的发展&#xff0c;人们越来越重视数字电视的质量问题&#xff0c;数字电视质量的好坏首先是数字电视信号的质量&#xff0c;因此数字电视信号的分析、测试非常重要&#xff0c;本文重点对数字电视信号的MER及星座图剖析。1、广义…

关于ASP.NET 中的主题

2019独角兽企业重金招聘Python工程师标准>>> 网站的外观是否美观将直接决定其受欢迎的程度&#xff0c;这就意味着网站在开发过程中设计和实现美观实用的用户界面是非常重要的。 在ASP.net 2.0之前主要是用样式表css来实现外观设计。但在ASP.Net 2.0之后&#xff0c…

傅里叶变换matlab案例,基于matlab的傅里叶变换

例子1作用&#xff1a;使用傅里叶变换找出隐藏在噪声中的信号的频率成分。(指定信号的参数&#xff0c;采样频率为1 kHz&#xff0c;信号持续时间为1秒。)Fs 1000; % 采样频率T 1/Fs; % 采样周期L 1000; % 信号长度t (0:L-1)*T; % 时间向量%%形成一个信号&#xff0c;包含振…

WEB渗透—PHP反序列化(八)

Web渗透—PHP反序列化 课程学习分享&#xff08;课程非本人制作&#xff0c;仅提供学习分享&#xff09; 靶场下载地址&#xff1a;GitHub - mcc0624/php_ser_Class: php反序列化靶场课程&#xff0c;基于课程制作的靶场 课程地址&#xff1a;PHP反序列化漏洞学习_哔哩…

如何部署 Hyperic ,使得从内网监测外网服务器

2019独角兽企业重金招聘Python工程师标准>>> 环境介绍&#xff1a; 外网服务器&#xff1a; www.InnovateDigital.com 用户名/密码 test/test 内网服务器&#xff1a;192.168.1.125 操作系统都是 Centos 7 64bit 过程&#xff1a; 下载 Hyperic 5.8.5 &#xff0c…

tcp 发送 最大数据量_网络基础知识夯实总结(三):TCP协议

近期分享的网络知识包括HTTP协议、DNS协议、HTTPS协议、TCP协议、IP协议、TCP/IP、Web攻击及其他协议。今天内容是TCP协议。TCP协议1. 传输层2. 作用提供可靠的字节流服务3. 大块数据分割成报文段(segment)4. 三次握手1) 发送端发带SYN标志的数据包给对方。2) 接收端收到后&…