什么是并发(非常详细)

news2024/11/27 4:26:19

按最简单、最基本的程度理解,并发(concurrency)是两个或多个同时独立进行的活动。并发现象遍布日常生活,我们可以边走路边说话,左右手同时做出不一样的动作,诸如此类。

计算机系统中的并发

若我们谈及计算机系统中的并发,则是指同一个系统中,多个独立活动同时进行,而非依次进行。

多年来,多任务操作系统可以凭借任务切换,让同一台计算机同时运行多个应用软件,这早已稀松平常,而高端服务器配备了多处理器,实现了“真并发”(genuine concurrency)。

大势所趋,主流计算机现已能够真真正正地并行处理多任务,而不再只是制造并发的表象。

很久之前,大多计算机都仅有一个处理器,处理器内只有单一处理单元或单个内核,许多台式计算机至今依旧如此。这种计算机在同一时刻实质上只能处理一个任务,不过,每秒内,它可以在各个任务之间多次切换,先处理某任务的一小部分,接着切换任务,同样只处理一小部分,然后对其他任务如法炮制。

看起来所有任务都正在同时执行,因此其被称为任务切换。至此,我们谈及的并发都基于这种模式。由于任务飞速切换,我们难以分辨处理器到底在哪一刻暂停某个任务而切换到另一个。任务切换对使用者和应用软件自身都制造出并发的表象。

由于是表象,因此对比真正的并发环境,当应用程序在进行任务切换的单一处理器环境下运行时,其行为可能稍微不同。具体而言,如果就内存模型做出不当假设,本来会导致某些问题,但这些问题在上述环境中却有可能不会出现。

多年来,配备了多处理器的计算机一直被用作服务器,它要承担高性能的计算任务;现今,基于一芯多核处理器(简称多核处理器)的计算机日渐普及,多核处理器也用在台式计算机上。

无论是装配多个处理器,还是单个多核处理器,或是多个多核处理器,这些计算机都能真正并行运作多个任务,我们称之为硬件并发(hardware concurrency)。

图 1 所示为理想化的情景:

d9612202307181732401938.png

图 1 两种并发方式:双核机上的并发执行与单核机上的任务切换
计算机有两个任务要处理,将它们进行十等分。在双核机(具有两个处理核)上,两个任务在各自的核上分别执行。另一台单核机则切换任务,交替执行任务小段,但任务小段之间略有间隔。

图 1 中,单核机的任务小段被灰色小条隔开,它们比双核机的分隔条粗大。为了交替执行,每当系统从某一个任务切换到另一个时,就必须完成一次上下文切换(context switch),于是耗费了时间。若要完成一次上下文切换,则操作系统需保存当前任务的 CPU 状态和指令指针,判定需要切换到哪个任务,并为之重新加载 CPU 状态。接着,CPU 有可能需要将新任务的指令和数据从内存加载到缓存,这或许会妨碍 CPU,令其无法执行任何指令,加剧延迟。

尽管多处理器或多核系统明显更适合硬件并发,不过有些处理器也能在单核上执行多线程。真正需要注意的关键因素是硬件支持的线程数(hardware threads),也就是硬件自身真正支持同时运行的独立任务的数量。

即便是真正支持硬件并发的系统,任务的数量往往容易超过硬件本身可以并行处理的数量,因而在这种情形下任务切换依然有用。譬如,常见的台式计算机能够同时运行数百个任务,在后台进行各种操作,表面上却处于空闲状态。正是由于任务切换,后台任务才得以运作,才容许我们运行许多应用软件,如文字处理软件、编译器、编辑软件,以及浏览器等。

cc5c9202307181733118725.png

图 2 展示了双核机上 4 个任务的相互切换,这同样是理想化的情形,各个任务都被均匀切分。实践中,许多问题会导致任务切分不均匀或调度不规则。

并发的方式

设想两位开发者要共同开发一个软件项目。假设他们处于两间独立的办公室,而且各有一份参考手册,则他们可以静心工作,不会彼此干扰。但这令交流颇费周章:他们无法一转身就与对方交谈,遂不得不借助电话或邮件,或是需起身离座走到对方办公室。另外,使用两间办公室有额外开支,还需购买多份参考手册。

现在,如果安排两位开发者共处一室,他们就能畅谈软件项目的设计,也便于在纸上或壁板上作图,从而有助于交流设计的创意和理念。这样,仅有一间办公室要管理,并且各种资源通常只需一份就足够了。但缺点是,他们恐怕难以集中精神,共享资源也可能出现问题。

这两种安排开发者的办法示意了并发的两种基本方式:

  • 一位开发者代表一个线程,一间办公室代表一个进程。第一种方式采用多个进程,各进程都只含单一线程,情况类似于每位开发者都有自己的办公室;
  • 第二种方式只运行单一进程,内含多个线程,正如两位开发者同处一间办公室。
    我们可以随意组合这两种方式,掌控多个进程,其中有些进程包含多线程,有些进程只包含单一线程,但基本原理相同。接着,我们来简略看看应用软件中的这两种并发方式。

多进程并发

在应用软件内部,一种并发方式是,将一个应用软件拆分成多个独立进程同时运行,它们都只含单一线程,非常类似于同时运行浏览器和文字处理软件。这些独立进程可以通过所有常规的进程间通信途径相互传递信息(信号、套接字、文件、管道等),如图 3 所示。

53ae3202307181733493493.png

这种进程间通信普遍存在短处:或设置复杂,或速度慢,甚至二者兼有,因为操作系统往往要在进程之间提供大量防护措施,以免某进程意外改动另一个进程的数据;还有一个短处是运行多个进程的固定开销大,进程的启动花费时间,操作系统必须调配内部资源来管控进程,等等。

进程间通信并非一无是处:通常,操作系统在进程间提供额外保护和高级通信机制。这就意味着,比起线程,采用进程更容易编写出安全的并发代码。某些编程环境以进程作为基本构建单元,其并发效果确实一流,譬如为 Erlang 编程语言准备的环境。

运用独立的进程实现并发,还有一个额外优势——通过网络连接,独立的进程能够在不同的计算机上运行。这样做虽然增加了通信开销,可是只要系统设计精良,此法足以低廉而有效地增强并发力度,改进性能。

多线程并发

另一种并发方式是在单一进程内运行多线程。线程非常像轻量级进程,每个线程都独立运行,并能各自执行不同的指令序列。

不过,同一进程内的所有线程都共用相同的地址空间,且所有线程都能直接访问大部分数据。全局变量依然全局可见,指向对象或数据的指针和引用能在线程间传递。

尽管进程间共享内存通常可行,但这种做法设置复杂,往往难以驾驭,原因是同一数据的地址在不同进程中不一定相同。图 4 展示了单一进程内的两个线程借共享内存通信。

086a220230718173412835.png

图 4 单一进程内的两个线程借共享内存通信
我们可以启用多个单线程的进程并在进程间通信,也可以在单一进程内发动多个线程而在线程间通信,后者的额外开销更低。因此,即使共享内存带来隐患,主流语言大都青睐以多线程的方式实现并发功能。

提到多线程代码,还常常用到一个词——并行。接下来,我们来厘清并发与并行的区别。

并发与并行

就多线程代码而言,并发与并行(parallel)的含义很大程度上相互重叠。确实,在多数人看来,它们就是相同的。

并发和并行的差别甚小,主要是着眼点和使用意图不同。两个术语都是指使用可调配的硬件资源同时运行多个任务,但并行更强调性能。当人们谈及并行时,主要关心的是利用可调配的硬件资源提升大规模数据处理的性能;当谈及并发时,主要关心的是分离关注点或响应能力。

为分离关注点而并发

一直以来,编写软件时,分离关注点(separation of concerns)几乎总是不错的构思:归类相关代码,隔离无关代码,使程序更易于理解和测试,因此所含缺陷很可能更少。

并发技术可以用于隔离不同领域的操作,即便这些不同领域的操作需同时进行;若不直接使用并发技术,我们将不得不编写框架做任务切换,或者不得不在某个操作步骤中,频繁调用无关领域的代码。

考虑一个带有用户界面的应用软件,需要由 CPU 密集处理,如台式计算机上的 DVD 播放软件。本质上,这个应用软件肩负两大职责:既要从碟片读取数据,解码声音影像,并将其及时传送给图形硬件和音效硬件,让 DVD 顺畅放映,又要接收用户的操作输入,譬如用户按“暂停”、“返回选项单”、“退出”等键。假若采取单一线程,则该应用软件在播放过程中,不得不定时检查用户输入,结果会混杂播放 DVD 的代码与用户界面的代码。

改用多线程就可以分离上述两个关注点,一个线程只负责用户界面管理,另一个线程只负责播放 DVD,用户界面的代码和播放 DVD 的代码遂可避免紧密纠缠。两个线程之间还会保留必要的交互,例如按“暂停”键,不过这些交互仅仅与需要立即处理的事件直接关联。

如果用户发送了操作请求,而播放 DVD 线程正忙,无法马上处理,那么在请求被传送到该线程的同时,代码通常能令用户界面线程立刻做出响应,即便只是显示光标或提示“请稍候”。这种方法使得应用软件看起来响应及时。类似地,某些必须在后台持续工作的任务,则常常交由独立线程负责运行,例如,让桌面搜索应用软件监控文件系统变动。此法基本能大幅简化各线程的内部逻辑,原因是线程间交互得以限定于代码中可明确辨识的切入点,而无须将不同任务的逻辑交错散置。

这样,线程的实际数量便与 CPU 既有的内核数量无关,因为用线程分离关注点的依据是设计理念,不以增加运算吞吐量为目的。

为性能而并行:任务并行和数据并行

多处理器系统已存在数十年,不过一直以来它们大都只见于巨型计算机、大型计算机和大型服务器系统。但是,芯片厂家日益倾向设计多核芯片,在单一芯片上集成 2 个、4 个、16 个或更多处理器,从而使其性能优于单核芯片。于是,多核台式计算机日渐流行,甚至多核嵌入式设备亦然。

不断增强的算力并非得益于单个任务的加速运行,而是来自多任务并行运作。从前,处理器更新换代,程序自然而然随之加速,程序员可以“坐享其成,不劳而获”。但现在,正如 Herb Sutter 指出的“免费午餐没有了!”,软件若要利用增强的这部分算力,就必须设计成并发运行任务。所以程序员必须警觉,特别是那些踌躇不前、忽视并发技术的同业,有必要注意熟练掌握并发技术,储备技能。

增强性能的并行方式有两种,分别是任务并行和数据并行。

第一种,最直观地,将单一任务分解成多个部分,各自并行运作,从而节省总运行耗时。此方式即为任务并行。尽管听起来浅白、直接,但这却有可能涉及相当复杂的处理过程,因为任务各部分之间也许存在纷繁的依赖。任务分解可以针对处理过程,调度某线程运行同一算法的某部分,另一线程则运行其他部分;也可以针对数据,线程分别对数据的不同部分执行同样的操作,这被称为数据并行。

易于采用上述并行方式的算法常常被称为尴尬并行算法。其含义是,将算法的代码并行化实在简单,甚至简单得会让我们尴尬,实际上这是好事。我还遇见过用其他术语描述这类算法,叫“天然并行”(naturally parallel)与“方便并发”(conveniently concurrent)。尴尬并行算法具备的优良特性是可按规模伸缩——只要硬件支持的线程数目增加,算法的并行程度就能相应提升。这种算法是成语“众擎易举”的完美体现。算法中除尴尬并行以外的部分,可以另外划分成一类,其并行任务的数目固定(所以不可按规模伸缩)。

第二种增强性能的并行方式是利用并行资源解决规模更大的问题。

例如,只要条件适合,便同时处理 2 个文件,或者 10 个,甚至 20 个,而不是每次 1 个。同时对多组数据执行一样的操作,实际上是采用了数据并行,其着眼点有别于任务并行。采用这种方式处理单一数据所需的时间依旧不变,而同等时间内能处理的数据相对更多。这种方式明显存在局限,虽然并非任何情形都会因此受益,但数据吞吐量却有所增加,进而带来突破。例如,若能并行处理视频影像中不同的区域,就会提升视频处理的解析度。

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

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

相关文章

图书馆管理的好帮手:Librarian Pro Mac 让你的阅读体验更加完美

Librarian Pro Mac是一款功能齐全、易于使用的图书馆管理软件,适用于个人用户、图书馆、学校和其他组织。它提供了丰富的功能和灵活的组织方式,帮助用户轻松管理和浏览他们的图书馆和收藏品。无论你是一个热衷于阅读的个人,还是一个需要管理大…

Linux小程序——进度条【Linux系统编程】

回车换行:回车(\r)是回到当前光标所在行的最开始,换行(\n)是换到下一行,回车换行就是到下一行的最开始。 缓冲区: 先看第一种:helloworld后面有反斜杠n,那么我们看到的现象是先打印…

01-复杂度2 Maximum Subsequence Sum

思路 比上一题(01-复杂度1 最大子列和问题),要多记录几个内容 首尾元素,当前子序列开头元素,当前子序列结尾元素,最佳子序列开头元素,current是否在此处清零(则下一个元素是开头元…

2023年四川大学生程序设计竞赛-A.旷野之息

题目描述 Cuber QQ 终于打败盖农救回了塞尔达公主,海拉鲁大地也开始灾后重建。 在统计学中,幂律表示的是两个量之间的函数关系,其中一个量的相对变化会导致另一个量的相应幂次比例的变化,且与初值无关:表现为一个量是…

注释气泡图函数(更新)

之前我们写过一个原创可视化函数Dotplot_anno.R,nature级别图表:一个注释气泡热图函数(适用于单细胞及普通数据)。主要解决的问题是1) 单细胞基因可视化分组注释。2) Bulk RNA差异基因热图、气泡图。3) 富集分析结果气泡图展示。这…

使用原生Redis命令实现分布式锁

推荐文章: 1、springBoot对接kafka,批量、并发、异步获取消息,并动态、批量插入库表; ​ 2、SpringBoot用线程池ThreadPoolTaskExecutor异步处理百万级数据; 3、java后端接口API性能优化技巧 4、SpringBootMyBatis流式查询,处理大规模数据,提高系统的性能和响应…

【雕爷学编程】Arduino动手做(22)——8X8 LED点阵MAX7219屏8

37款传感器与模块的提法,在网络上广泛流传,其实Arduino能够兼容的传感器模块肯定是不止37种的。鉴于本人手头积累了一些传感器和模块,依照实践出真知(一定要动手做)的理念,以学习和交流为目的,这…

Failed to initialize NVML: Driver/library version mismatch (解决)

问题描述 运行nvidia-smi报错: Failed to initialize NVML: Driver/library version mismatch解决方法 只需一步:下载一个安装包,运行一个命令来重新安装cuda driver和cuda toolkit(在一个包里)。 到这里&#xff1…

JVS开源基础框架:用户管理介绍(支持同步钉钉、企微、微信等)

在企业内部系统中,用户管理是指对系统内的用户进行管理、授权和权限管理的过程,这里主要介绍用户的创建与基本信息的管理,权限、登录等详细介绍请参考相关章节。 用户管理界面 点击平台管理-用户管理,界面上展示了组织管理与组织…

磁盘分区形式MBR与GPT介绍

磁盘分区形式MBR与GPT介绍 磁盘分区形式有两种: 1、MBR(主启动记录)形式,它是存在于磁盘驱动器开始部分的一个特殊的启动扇区; 2、GPT(GUID分区表)形式,它是一种使用UEFI启动的磁盘…

网络操作系统详解

网络操作系统的概念 操作系统是计算机系统中用来管理各种软硬件资源,提供人机交互使用的软件。网络操作系统可实现操作系统的所有功能,并且能够对网络中的资源进行管理和共享。网络操作系统(Network Operation System,NOS)是使网络上各种计算…

ai绘画怎么弄?这份ai绘画教程你必须知道

我有一个关系非常好的女性朋友,她的闺蜜即将过生日,她想给她闺蜜准备一份特别的礼物。但是,她对绘画毫无天赋。因此,我给她推荐了几款好用的ai绘画工具,这些神奇的ai绘画工具能帮助她创作出栩栩如生、美丽动人的女生形…

从零开始基于go-zero的go web项目实战-01项目初始化

从零开始基于go-zero搭建go web项目实战-01项目初始化 简介 导语 Go 是 Google 开发的一种静态强类型、编译型、并发型,并具有垃圾回收功能的编程语言,go语言的特点: 语法简洁Go语言简单易学,学习曲线平缓代码风格统一执行性能…

Spring Boot进阶(56):使用 ECharts 绘制各种花哨的统计图 | 超级详细,建议收藏

1. 前言🔥 在前几期,我们重点学习了如何在Spring Boot中使用模板引擎Thymeleaf开发Web应用的基础。接下来,我们介绍一下后端开发经常会遇到的一个场景:可视化图表。比如如下: 通常啊,这类业务在客户端应用中…

Meta与微软联手推出开源大型语言模型Llama 2;程序员如何优雅地做副业

🦉 AI新闻 🚀 Meta与微软联手推出开源大型语言模型Llama 2 摘要:Meta和微软近期合作发布了名为Llama 2的开源大型语言模型。该模型旨在帮助开发者和组织构建生成式人工智能工具和体验。Azure客户可以更轻松、安全地在Azure平台上微调和部署…

6、Java入门教程【数组】

数组是用于存储同种类型的多个数据的容器。 一、声明 //语法 dataType[] arrayRefVar; // 首选的方法 或 dataType arrayRefVar[]; // 效果相同,但不是首选方法//示例 double[] myList; // 首选的方法 或 double myList[]; // 效果相同&…

7.6Java EE——Bean的生命周期

Bean在不同作用域内的生命周期 Bean的生命周期是指Bean实例被创建、初始化和销毁的过程。在Bean的两种作用域singleton和prototype中,Spring容器对Bean的生命周期的管理是不同的。在singleton作用域中,Spring容器可以管理Bean的生命周期,控制…

2023 年最全的 5 款免费 PDF 编辑器

这里,我们回顾并比较了一些最好的开源 PDF 编辑器,以及每个编辑器的主要功能和优缺点: 寻找免费的 PDF 编辑器很困难,特别是如果您正在寻找开源 PDF 编辑器。但您不必担心在线搜索 PDF 编辑器的研究和分析,因为我们已…

Apikit 自学日记:测试模板

在 APIKIT 中,你可以将重复的测试步骤添加到测试模板库中,并且在测试用例中引用测试模板来实现复用测试步骤的目的。如将用户登录、清理数据库等操作作为测试模板,并将该用例引入到多个测试用例中,减少不必要的工作。 添加测试模板…

通达信组合副图指标公式-通达信公式

A:REF((C>O),2); A1:REF((C>O),1); A2:C>O OR CO; A3:REF(O,1)<REF(C,2); A4:O<REF(C,1); A5:REF((C-O),1)<REF((C-O),2); A6:(C-O)<REF((C-O),1); A7:REF(C,2)>REF(O,7)*1.05; 节奏1:A AND A1 AND A2 AND A3 AND A4 AND A5 AND A6 AND A7; 节奏2:A AN…