2022-03-25
大型的软件项目常常包含非常多的任务需要处理。例如:对于大量数据的数据流处理,或者是包含复杂GUI界面的应用程序。如果将所有的任务都以串行的方式执行,则整个系统的效率将会非常低下,应用程序的用户体验会非常的差。
另一方面,自上个世纪六七十年代英特尔创始人之一 Gordon Moore 提出 摩尔定义 以来,CPU频率以每18个月翻一番的指数速度增长。但这一增长在最近的十年已经基本停滞,大家会发现曾经有过一段时间CPU的频率从3G到达4G,但在这之后就停滞不前了。因此最近的新款CPU也基本上都是3G左右的频率。相应的,CPU以更多核的形式在增长。目前的Intel i7有8核的版本,Xeon处理器达到了28核。并且,最近几年手机上使用的CPU也基本上是4核或者8核的了。
由此,掌握并发编程技术,利用多处理器来提升软件项目的性能将是软件工程师的一项基本技能。
本文以C++语言为例,讲解如何进行并发编程。并尽可能涉及C++11,C++14以及C++17中的主要内容。
并发(Concurrent)与并行(Parallel)都是很常见的术语。
Erlang之父Joe Armstrong曾经以人们使用咖啡机的场景为例描述了这两个术语。如下图所示:
并发:如果多个队列可以交替使用某台咖啡机,则这一行为就是并发的。
并行:如果存在多台咖啡机可以被多个队列交替使用,则就是并行。
这里队列中的每个人类比于计算机的任务,咖啡机类比于计算机处理器。因此:并发和并行都是在多任务的环境下的讨论。
更严格的来说:如果一个系统支持多个动作同时存在,那么这个系统就是一个并发系统。如果这个系统还支持多个动作(物理时间上)同时执行,那么这个系统就是一个并行系统。
你可能已经看出,“并行”其实是“并发”的子集。它们的区别在于是否具有多个处理器。如果存在多个处理器同时执行多个线程,就是并行。
在不考虑处理器数量的情况下,我们统称之为“并发”。
进程与线程是操作系统的基本概念。无论是桌面系统:MacOS,Linux,Windows,还是移动操作系统:Android,iOS,都存在进程和线程的概念。
进程(英语:process),是指计算机中已运行的程序。进程为曾经是分时系统的基本运作单位。在面向进程设计的系统(如早期的UNIX,Linux 2.4及更早的版本)中,进程是程序的基本执行实体;
线程(英语:thread)是操作系统能够进行运算调度的最小单位。它被包含在进程之中,是进程中的实际运作单位。
-- 维基百科
关于这两个概念在任何一本操作系统书上都可以找到定义。网上也有很多文章对它们进行了解释。因此这里不再赘述,这里仅仅提及一下它们与编程的关系。
对于绝大部分编程语言或者编程环境来说,我们所写的程序都会在一个进程中运行。一个进程至少会包含一个线程。这个线程我们通常称之为主线程。
在默认的情况下,我们写的代码都是在进程的主线程中运行,除非开发者在程序中创建了新的线程。
不同编程语言的线程环境会不一样,Java语言在很早就支持了多线程接口。(Java程序在Java虚拟机中运行,虚拟机通常还会包含自己特有的线程,例如垃圾回收线程。)。而对于JavaScript这样的语言来说,它就没有多线程的概念。
当我们只有一个处理器时,所有的进程或线程会分时占用这个处理器。但如果系统中存在多个处理器时,则就可能有多个任务并行的运行在不同的处理器上。
下面两幅图以不同颜色的矩形代表不同的任务(可能是进程,也可能是线程)来描述它们可能在处理器上执行的顺序。
下图是单核处理器的情况:
下面是四核处理器的情况:
任务会在何时占有处理器,通常是由操作系统的调度策略决定的。在《Android系统上的进程管理:进程的调度》一文中,我们介绍过Linux的调度策略。
当我们在开发跨平台的软件时,我们不应当对调度策略做任何假设,而应该抱有“系统可能以任意顺序来调度我的任务”这样的想法。
开发并发系统最主要的动机就是提升系统性能(事实上,这是以增加复杂度为代价的)。
但我们需要知道,单纯的使用多线程并不一定能提升系统性能(当然,也并非线程越多系统的性能就越好)。从上面的两幅图我们就可以直观的感受到:线程(任务)的数量要根据具体的处理器数量来决定。假设只有一个处理器,那么划分太多线程可能会适得其反。因为很多时间都花在任务切换上了。
因此,在设计并发系统之前,一方面我们需要做好对于硬件性能的了解,另一方面需要对我们的任务有足够的认识。
关于这一点,你可能需要了解一下阿姆达尔定律了。对于这个定律,简单来说:我们想要预先意识到那些任务是可以并行的,那些是无法并行的。只有明确了任务的性质,才能有的放矢的进行优化。这个定律告诉了我们将系统并行之后性能收益的上限。
关于阿姆达尔定律在Linux系统监测工具sysstat介绍一文中已经介绍过,因此这里不再赘述。
前面我们已经了解到,并非所有的语言都提供了多线程的环境。
即便是C++语言,直到C++11标准之前,也是没有多线程支持的。在这种情况下,Linux/Unix平台下的开发者通常会使用POSIX Threads,Windows上的开发者也会有相应的接口。但很明显,这些API都只针对特定的操作系统平台,可移植性较差。如果要同时支持Linux和Windows系统,你可能要写两套代码。
相较而言,Java自JDK 1.0就包含了多线程模型。
这个状态在C++ 11标准发布之后得到了改变。并且,在C++ 14和C++ 17标准中又对并发编程机制进行了增强。
下图是最近几个版本的C++标准特性的线路图。
编译器对于语言特性的支持是逐步完成的。想要使用特定的特性你需要相应版本的编译器。
GCC对于C++特性的支持请参见这里:C++ Standards Support in GCC。
Clang对于C++特性的支持请参见这里:C++ Support in Clang。
下面两个表格列出了C++标准和相应编译器的版本对照:
C++标准与相应的GCC版本要求如下:
C++标准与相应的Clang版本要求如下:
默认情况下编译器是以较低的标准来进行编译的,如果希望使用新的标准,你需要通过编译参数-std=c++xx告知编译器,例如:
g++ -std=c++17 your_file.cpp -o your_program本文的源码可以到下载我的github上获取,地址:paulQuei/cpp-concurrency。
你可以直接通过下面这条命令获取源码:
git clone https://github.com/paulQuei/cpp-concurrency.git源码下载之后,你可以通过任何文本编辑器浏览源码。如果希望编译和运行程序,你还需要按照下面的内容来准备环境。
本文中的源码使用cmake编译,只有cmake 3.8以上的版本才支持C++ 17,所以你需要安装这个或者更新版本的cmake。
另外,截止目前(2019年10月)为止,clang编译器还不支持并行算法。
但是gcc-9是支持的。因此想要编译和运行这部分代码,你需要安装gcc 9.0或更新的版本。并且,gcc-9还要依赖Intel Threading Building Blocks才能使用并行算法以及头文件。
具体的安装方法见下文。
具体编译器对于C++特性支持的情况请参见这里:C++ compiler support。
安装好之后运行根目录下的下面这个命令即可:
./make_all.sh它会完成所有的编译工作。
本文的源码在下面两个环境中经过测试,环境的准备方法如下。
在Mac上,我使用brew工具安装gcc以及tbb库。
考虑到其他人与我的环境可能会有所差异,所以需要手动告知tbb库的安装路径。
读者需要执行下面这些命令来准备环境:
rew install gccbrew insbtall tbb export tbb_path=/usr/local/Cellar/tbb/2019_U8/./make_all.sh注意,请通过运行g++-9命令以确认gcc的版本是否正确,如果版本较低,则需要通过brew命令将其升级到新版本:
brew upgrade gccUbuntu上,通过下面的命令安装gcc-9。
sudo add-apt-repository ppa:ubuntu-toolchain-r/testsudo apt-get updatesudo apt install gcc-9 g++-9但安装tbb库就有些麻烦了。这是因为Ubuntu 16.04默认关联的版本是较低的,直接安装是无法使用的。我们需要安装更新的版本。
联网安装的方式步骤繁琐,所以可以通过下载包的方式进行安装,我已经将这需要的两个文件放到的这里:
libtbb2_2019~U8-1_amd64.deb
libtbb-dev_2019~U8-1_amd64.deb
如果需要,你可以下载后通过apt命令安装即可:
sudo apt install ~/Downloads/libtbb2_2019~U8-1_amd64.deb sudo apt install ~/Downloads/libtbb-dev_2019~U8-1_amd64.deb创建线程非常的简单的,下面就是一个使用了多线程的Hello World示例:
// 01_hello_thread.cpp #include #include // ① using namespace std; // ② void hello() { // ③ cout最速三秒钟!希捷4TB移动硬盘非暴力极速拆解权威教程
2022-03-26
戴尔Inspiron 灵越7572怎么重装win10系统
2022-03-26
联想y700加装nvme固态bios不认别解决方法及win7安装教程
2022-03-26
国内大神成功给自己手机装上了 Win11操作系统,附方法!
2022-03-26
192.168.36.1手机登陆入口页面
2022-03-26
华为电脑预装国产系统,取代微软Windows,可惜不是鸿蒙
2022-02-17
神舟bios设置图解教程
2022-03-26
联想小新Air 14笔记本重装系统WIN10教程(重装win10专业版)
2022-03-26
联想笔记本电脑清理灰尘详细步骤
2022-03-26
ACPI是什么?BIOS中怎么设置ACPI?
2022-03-26
上古修仙最新版下载v10.2.29 安卓版
角色扮演 180.1M
下载最强NBA官方版下载v1.55.662 安卓版
体育运动 1.68G
下载逃跑吧少年官方正版手游下载v8.33.0 安卓2025手机版
动作闯关 1.33G
下载宝宝梦想小镇宝宝巴士最新版下载v9.87.00.00 安卓官方版
其它手游 95.9M
下载宝宝爱刷牙宝宝巴士教育版下载v9.87.00.00 安卓版
其它手游 148.0M
下载小玛丽捕鱼官方正版下载v5.13.0 安卓版
休闲益智 122.0M
下载小玛丽捕鱼旧版安装下载v5.10.10 安卓官方版
休闲益智 122.0M
下载原神bilibili服官方版下载v5.8.0 安卓哔哩哔哩服
其它手游 389.7M
下载天天捕鱼电玩版赢话费官方版下载v11.0 安卓版
下载
遇见龙2手游下载v1.1.157 安卓版
下载
太空杀国际版(Super Sus)下载v1.67.25.031 安卓版
下载
变形金刚地球之战官方版下载v25.1.1.381 安卓最新版
下载
极限捕鱼安卓版下载v6.47.6.0.5.4 赢话费正版
下载
极限捕鱼高爆版游戏下载v6.47.6.0.5.4 安卓暴击版
下载
极限捕鱼万炮版下载v6.47.6.0.5.4 安卓加强版
下载
极限捕鱼达人版下载v6.47.6.0.5.4 安卓版
下载