并行编程和并行编程有什么区别?


346

并行编程和并行编程有什么区别?我问谷歌,但没有找到任何可以帮助我理解这种差异的东西。你能给我两个例子吗?

现在,我找到了以下解释:http : //www.linux-mag.com/id/7411-但是“并发是程序的属性”与“并行执行是机器的属性”对我来说还不够-我还是不能说什么。


Answers:


310

如果您使用线程(并行编程)编程,则不一定要这样执行(并行执行),因为这取决于计算机是否可以处理多个线程。

这是一个视觉示例。非线程机器上的线程

        --  --  --
     /              \
>---- --  --  --  -- ---->>

线程计算机上的线程

     ------
    /      \
>-------------->>

破折号代表执行的代码。如您所见,它们既拆分又分别执行,但是线程机可以一次执行几个单独的部分。


34
并行执行和并行编程不是一回事。乔恩·哈罗普(Jon Harrop)的答案是正确的。但是问题似乎使并行执行和并行编程变得混乱。
Blaisorblade

3
并行执行线程的能力不仅仅取决于机器。例如,由于对垃圾收集器的全局锁定,OCaml(和Python?)可同时执行线程,但不能并行执行线程。
乔恩·哈罗普

1
根据此博客,并行编程不是并发编程的子集;您的回答没有考虑到这一点,您对此说法有何看法?
凯文

1
@Kevin:我认为“更笼统”的意思是超集。我同意这是错误的。
乔恩·哈罗普

1
该答案有助于可视化并发执行与并行执行之间的区别,但不适用于发帖人关于编程的原始问题。
Reorx

396

并发编程考虑到似乎重叠的操作,并且主要涉及由于不确定性控制流而引起的复杂性。与并发程序相关的量化成本通常是吞吐量和延迟。并发程序通常受IO约束,但并不总是如此,例如,并发垃圾收集器完全在CPU上。并发程序的教学示例是Web搜寻器。该程序启动对网页的请求,并在下载结果可用时并发接受响应,从而累积一组已被访问的页面。控制流是不确定的,因为每次运行程序时不一定都以相同的顺序接收响应。这种特性会使调试并发程序变得非常困难。Erlang,F#异步工作流和Scala的Akka库可能是高度并发编程的最有前途的方法。

多核编程是并行编程的特例。并行编程涉及为了提高吞吐量的特定目标而重叠的操作。通过确定控制流可以避免并发编程的困难。通常,程序会生成并行运行的子任务集,并且父任务仅在每个子任务完成后才继续。这使得并行程序比并发程序更容易调试。并行编程的难点是针对诸如粒度和通信之类的问题进行性能优化。在多核的情况下,后者仍然是一个问题,因为与将数据从一个缓存传输到另一个缓存相关的成本很高。Cilk可能是在多核上进行高性能并行编程的最有前途的方法,并且已在英特尔的线程构建模块和Microsoft的任务并行库(在.NET 4中)中采用。


18
“并行编程的困难部分,例如粒度和通信。” 如果并行任务需要通信,那不是使它们并行吗?
贾斯汀·凯斯

13
“如果并行任务需要通信,那不是使它们并行吗?”。哇,好问题!不一定,不。超级计算机通常使用批量并行操作进行编程,然后进行数据的全局重新分配和更多批量并行性。因此,存在并行性和通信性,但是没有真正的并发性。在这种情况下,我更多地考虑了多核并行性,其中通信意味着高速缓存复杂性,例如高速缓存一致性所需的通信。尽管这是并发的,但它也不是直接可见的。
乔恩·哈罗普

43
@BoppityBop仅仅因为我可以在绘画中说出他在小说中所说的话,并不能使我的答案变得正确。对于实际上不知道答案的人来说,阅读起来更容易。我想这是来到这里的重点。您可以用本文所用的语言来写书,但这对大多数读者来说绝对是轻描淡写的,因为如果您已经知道jon所写内容的一半,那么您可能不会用谷歌搜索这个问题。
Tor Valamo 2014年

18
这张图片对我非常有帮助,对于这个话题来说是个新手,@ JonHarrop的描述对我很有用,即使是技术性的语言,他们也喜欢正确的语言。这两个答案有助于我更全面地理解。我们都赢了!(尽管我很欣赏并行执行和并行编程之间的区别)
Sammaron 2014年

3
“ Erlang也许是最有前途的即将到来的语言……”。有趣的单词选择,因为Erlang大约30岁,并于1998
。– steinar '16

151

https://joearms.github.io/published/2013-04-05-concurrent-and-parallel-programming.html

并发=两个队列和一台咖啡机。

并行=两个队列和两个咖啡机。


9
错误和误导。并发=允许一个或多个队列(不确定的组成)。并行= 如果不为空,则具有多个队列以使其中任何一个队列比原始队列(渐近效率)。
FrankHB '18

并发代码需要两个或更多处理器(或“咖啡机”)。因此,这个答案本质上是错误的。
Geoffrey Anderson

6
@GeoffreyAnderson不,不是。例如,线程和进程在单个核心计算机上同时执行。
乔恩·哈罗普

@FrankHB-请访问stackoverflow.com/a/57223044/1406510,并查看源链接-在Oracle网站上-这样做没错,但我们的理解可以。所以是时候重新考虑了。看完之后我确实改变了看法。
nanosoft

@GeoffreyAnderson-请查看stackoverflow.com/a/57223044/1406510。它包含来自oracle的链接,并清楚说明了什么。因此需要与之保持一致。
nanosoft

40

将原始问题解释为并行/并发计算,而不是编程

并发计算中,两个计算都彼此独立地进行。第二个计算不必等到第一个计算完成就可以进行。但是,它没有说明如何实现此目标的机制。在单核设置中,需要在线程之间挂起和交替(也称为抢先式多线程)。

并行计算中,两个计算都同时进行 -实际上是在同一时间。单CPU无法实现,而是需要多核设置。

图片来自文章:“ Node.js中的并行与并发”

暂停并轮流并行计算



21

我相信并发编程是指多线程编程,它是让您的程序运行从硬件详细信息中抽象出来的多个线程。

并行编程是指专门设计程序算法以利用可用的并行执行。例如,您可以并行执行某些算法的两个分支,以期比起先检查第一个然后再检查第二个分支的结果更快(平均)达到结果。


2
换句话说,并行执行两件事可以使它们完成的速度快两倍。如果同时执行两件事,则可能需要花费与第一件事相同的时间,然后再执行另一件事,如果在运行第一件事和第二件事之间只有一个CPU时间片来回切换,等等
user189169 09年

14

我在一些博客中找到了此内容。认为它是有用和相关的。

并发和并行性不是一回事。如果未预先确定两个任务的执行顺序,则两个任务T1和T2是并发的,

T1可以在T2之前执行并完成,T2可以在相同时间(并行)同时执行T1,T1和T2之前执行并完成,T1和T2可以交替执行,...如果有两个并发线程如果操作系统将其调度为在一个单核非SMT非CMP处理器上运行,则可能会并发但不会并行。并行可能在多核,多处理器或分布式系统上进行。

并发通常被称为程序的属性,并且是比并行性更笼统的概念。

资料来源:https : //blogs.oracle.com/yuanlin/entry/concurrency_vs_parallelism_concurrent_programming


9

他们是两个短语,从(非常轻微)不同的角度描述同一件事。并行编程从硬件的角度描述了这种情况-至少有两个处理器(可能在单个物理包中)并行处理问题。并发编程从软件的角度描述了更多东西-可能在同一时间(同时)发生两个或多个动作。

这里的问题是,当人们不真正使用这两个短语时,他们会尝试使用这两个短语进行明确的区分。现实情况是,他们试图绘制的分界线数十年来一直模糊不清,并且随着时间的推移变得越来越模糊。

他们试图讨论的事实是,从前,大多数计算机只有一个CPU。当您在单个CPU上执行多个进程(或线程)时,CPU一次实际上仅在其中一个线程中执行一条指令。并发的外观是一种错觉-CPU在不同线程之间执行指令之间的切换足够快,以至于让人感觉到(任何小于100 ms的瞬间看起来都是瞬时的),看起来好像它正在一次做很多事情。

与此形成鲜明对比的是,计算机具有多个CPU或具有多个内核的CPU,因此该计算机正好同时执行来自多个线程和/或进程的指令。执行一个的代码不会/不会对另一个执行的代码产生任何影响。

现在的问题是:如此清晰的区分几乎从未存在过。计算机设计师实际上是相当聪明的,所以他们很久以前就注意到(例如)当您需要从I / O设备(例如磁盘)读取某些数据时,花了长时间(就CPU周期而言)完。他们没有让CPU闲置,而是想出了各种方法让一个进程/线程发出I / O请求,并让其他进程/线程的代码在I / O请求完成时在CPU上执行。

因此,早在多核CPU成为规范之前,我们就有来自多个线程的操作并行发生。

不过,那只是冰山一角。几十年前,计算机也开始提供另一级别的并行性。再次,作为非常聪明的人,计算机设计师注意到,在很多情况下,他们所使用的指令不会互相影响,因此可以从同一流中同时执行多个指令。早已广为人知的一个例子是Control Data6600。这是1964年推出时(相当大幅度)世界上最快的计算机-至今仍使用许多相同的基本体系结构。它跟踪每条指令使用的资源,并具有一组执行单元,一旦它们依赖的资源可用,它们就会执行指令,这与最新的Intel / AMD处理器的设计非常相似。

但是(正如商业广告所说的那样)等等-还不止这些。还有另一个设计元素,进一步增加了混乱。它有很多不同的名称(例如“ Hyperthreading”,“ SMT”,“ CMP”),但它们都引用相同的基本思想:一个CPU可以同时使用多个资源并同时执行多个线程,每个线程和线程之间共享的一些资源是独立的。在典型情况下,这与上面概述的指令级并行性结合在一起。为此,我们有两组(或更多组)体系结构寄存器。然后,我们有一组执行单元,可以在必要的资源可用时立即执行指令。

然后,我们当然可以使用具有多个内核的现代系统。这里的事情很明显,对吧?我们有N个内核(目前在2到256个左右之间),每个内核都可以同时执行指令,因此我们有一个真正的并行处理的明显案例-在一个进程/线程中执行指令不会不会影响另一个执行指令。

好吧,有点。即使在这里,我们也有一些独立的资源(寄存器,执行单元,至少一个缓存级别)和一些共享资源(通常至少是最低缓存级别,并且肯定还有内存控制器和内存带宽)。

总结一下:人们喜欢在共享资源和独立资源之间进行对比的简单场景实际上在现实生活中从未发生过。在共享所有资源的情况下,我们最终会遇到类似于MS-DOS的情况,在该环境中,我们一次只能运行一个程序,并且必须停止运行一个程序才能完全运行另一个程序。有了完全独立的资源,我们有N台运行MS-DOS的计算机(甚至没有网络来连接它们),根本无法共享它们之间的任何内容(因为即使我们可以共享文件,那也就是共享资源,违反了什么都不共享的基本前提)。

每个有趣的案例都涉及独立资源和共享资源的某种组合。每台相当现代的计算机(以及很多不是现代的计算机)至少具有同时执行至少一些独立操作的能力,并且几乎任何比MS-DOS更复杂的事物都至少利用了这一点。一定程度上。

人们喜欢画的“并发”和“平行”之间的漂亮,清晰的划分是不存在的,而且几乎永远不会存在。人们喜欢归类为“并发”的内容通常仍然涉及至少一种并且通常是更多种不同类型的并行执行。他们喜欢归类为“并行”的内容通常涉及共享资源,(例如)一个进程在使用两者之间共享的资源时阻止了另一个进程的执行。

试图在“并行”和“并行”之间进行清晰区分的人们生活在幻想中的计算机实际上并不存在。


6
  • Concurrent programming从广义上讲,是指我们定义的任务可以按任何顺序发生的环境。一个任务可以在另一个任务之前或之后发生,并且某些或所有任务可以同时执行。

  • Parallel programming具体是指在不同处理器上同时执行并发任务。因此,所有并行编程都是并行的,但并非所有并行编程都是并行的。

资料来源:PThreads编程-更好的多处理的POSIX标准,Buttlar,Farrell,Nichols


5

在编程中,并发是独立执行的进程的组成,而并行性是(可能相关的)计算的同时执行。
-Andrew Gerrand-

并发是独立执行计算的组成。并发是一种结构化软件的方式,尤其是一种编写与现实世界良好交互的简洁代码的方式。这不是并行性。

并发不是并行性,尽管它支持并行性。如果只有一个处理器,则程序仍可以是并发的,但不能是并行的。另一方面,编写良好的并发程序可能会在多处理器上并行高效运行。该属性可能很重要...-
罗伯·派克-

为了了解两者之间的区别,我强烈建议您观看Rob Pike(Golang创作者之一)的视频。并发不是并行



5

当同时执行代码并且每次执行彼此独立时,将发生并行编程。因此,通常不会过分关注共享变量,因为这种情况不太可能发生。

但是,并发编程由共享变量的不同进程/线程执行的代码组成,因此,在并发编程中,我们必须建立某种规则来确定首先执行哪个进程/线程,我们希望这样做,以便可以确保在那里将保持一致,我们可以肯定地知道会发生什么。如果没有控制权,并且所有线程同时进行计算并将事物存储在相同的变量中,那么我们怎么知道最终会发生什么?也许一个线程比另一个线程快,也许其中一个线程甚至在执行过程中停止了运行,而另一个线程使用损坏的(尚未完全计算出的)变量继续执行不同的计算,则可能性是无限的。在这些情况下,我们通常使用并发编程而不是并行编程。


5

经典的任务调度可以是 串行并行并发

  • 串行:任务必须以已知的欺骗顺序一个接一个地执行,否则将无法正常工作。很简单。

  • 并行:必须同时执行任务,否则将无法执行。

    • 任何一项任务的任何失败(功能上或及时上)都会导致整个系统失败。
    • 所有任务必须具有共同的可靠时间感。

    尽量避免这种情况,否则下午茶时间我们会流泪。

  • 并发:我们不在乎。但是,我们并不是粗心的:我们已经分析过了,没关系;因此,我们可以随时使用任何可用工具执行任何任务。快乐的时光。

通常,可用调度会在已知事件(我们称为状态更改)时发生更改。

人们经常认为这与软件有关,但实际上,它是一种早于计算机的系统设计概念。软件系统的使用有点慢,甚至很少软件语言试图解决该问题。如果您有兴趣,可以尝试查找晶片机语言occam

简而言之,系统设计可解决以下问题:

  • 动词-您在做什么(运算或算法)
  • 名词-您在做什么(数据或接口)
  • 何时-启动,时间表,状态更改
  • 方式-串行,并行,并发
  • 哪里-一旦知道事情发生的时间,就可以说它们可以发生的地方,而不是之前。
  • 为什么-这是这样做的方法吗?还有其他方法,更重要的是,还有更好的方法吗?如果您不这样做会怎样?

祝好运。


8
我到处都看到帽子
Bruno Penteado 2014年

10
这个答案比并发和并行性这两个主题复杂得多。
凯·塞格伦2015年

3

我了解到的区别是:

1)并发-使用共享资源串联运行2)并行-使用不同资源并排运行

因此,即使它们在点(2)汇合在一起,也可以使彼此同时发生的两件事同时发生,或者在执行的整个操作中,两件事利用相同的储备金(1)。


3

尽管对并行并发之间的区别还没有达成完全的共识,但许多作者还是做出了以下区别:

  • 在并发计算中,程序是指可以随时执行多个任务的程序。
  • 在并行计算中,程序是其中多个任务紧密协作以解决问题的程序。

因此,并行程序是并发的,但是诸如多任务操作系统之类的程序也是并发的,即使它在只有一个内核的机器上运行,因为可以随时执行多个任务。

资料来源:并行编程入门,Peter Pacheco


1

并发和并行性 来源

在单个处理器上的多线程进程中,处理器可以在线程之间切换执行资源,从而导致并发执行

在共享内存多处理器环境中的同一多线程进程中,该进程中的每个线程可以同时在单独的处理器上运行,从而导致并行执行

当进程中的线程数少于或等于处理器时,线程支持系统与操作环境一起确保每个线程在不同的处理器上运行。

例如,在具有相同数量的线程和处理器的矩阵乘法中,每个线程(和每个处理器)计算一行结果。


此源只显示一个特殊的情况下实现 -多线程的一种特殊形式。是的,它甚至没有涵盖多线程的全部故事,例如M:N用户空间线程模型和线程调度的作用。就系统体系结构(OS,VM,启用HT的CPU等)和/或编程接口而言,线程只是实现的一种特殊方式。确实存在更多的东西,例如现代CPU的实现中的指令级并行性,它不公开任何编程接口,并且与线程无关。
FrankHB

@FrankHB:如果您可以共享有关您内容的任何真实链接,我将不胜感激。我真的很想探索是否还有更多。我目前的理解很简单-在具有给定线程调度机制的给定任何OS体系结构上运行多线程应用程序,这是并行的还是并发的?即使给定了M:N用户空间-您如何确定RUN是并行还是并发?
nanosoft

我已经写了一个答案来讨论不同抽象中的问题。
FrankHB

与基本抽象相比,运行多线程应用程序实际上非常复杂,因为“运行”是适合许多抽象的通用操作。在基本抽象上,实现中的线程模型(通常是用于对应用程序进行编程的语言规范和语言运行时实现)必须补充许多细节。
FrankHB

0

在许多不同的特定情况下,不同的人谈论不同种类的并发和并行性,因此需要一些抽象来掩盖其共同性质。

基本抽象是在计算机科学中完成的,其中并行性和并行性都归因于程序的属性。在这里,程序是对计算的形式化描述。这样的程序不必使用任何特定于实现的特定语言或编码。API / ABI / ISA / OS的存在与这种抽象级别无关。当然,将需要更详细的特定于实现的知识(例如线程模型)来进行具体的编程工作,基本抽象的精神不会改变。

第二个重要事实是,由于通用属性,并发和并行性可以在许多不同的抽象中共存

对于一般区别,请参阅并发与并行性基本视图的相关答案(也有一些包含其他来源的链接。)

并发编程和并行编程是使用某些暴露可编程性的系统来实现此类一般属性的技术。这些系统通常是编程语言及其实现。

编程语言可以通过内置语义规则公开预期的属性。在大多数情况下,此类规则指定对特定语言结构(例如表达式)的评估,从而使所涉及的计算有效地并发或并行。(更具体地说,评估所隐含的计算效果可以完美地反映这些属性。)但是,并发/并行语言语义本质上是复杂的,对于实际工作来说并不必要(将有效的并发/并行算法作为现实问题的解决方案来实现) )。因此,大多数传统语言都采用了更为保守和简单的方法:假设评估的语义是完全顺序和串行的,然后提供可选的原语以允许某些计算是并发的和并行的。这些原语可以是该语言支持的关键字或过程结构(“功能”)。它们是基于与托管环境(OS或“裸机”硬件接口)的交互来实现的,托管环境通常相对于该语言是不透明的(无法使用该语言可移植地派生)。因此,在程序员所看到的这种特殊的高级抽象中,除了这些“魔术”原语和依赖于这些原语的程序之外,并发/并行是没有什么。如果并发/并行性属性不那么令人感兴趣,程序员可以享受较少的易于出错的编程经验。

尽管基元在最高级的抽象中将复合体抽象化,但实现仍具有语言功能未暴露的额外复杂性。因此,需要一些中级抽象。一个典型的例子是线程。线程允许语言实现(运行时)支持一个或多个执行线程(或简称为线程;有时也称为进程,它不一定是操作系统中计划的任务的概念)。线程通常是由运行时抢先调度的,因此线程不需要了解其他线程。因此,只要不共享任何线程(关键资源,线程就很自然地实现并行性)):只需分解不同线程中的计算,一旦基础实现允许执行过程中计算资源的重叠,它就可以工作。线程还受共享资源的并发访问的约束:仅以任何顺序访问资源都将满足算法所需的最小约束,并且实现将最终确定何时访问。在这种情况下,可能需要执行一些同步操作。有些语言将线程和同步操作视为高级抽象的一部分,并将它们公开为原语,而其他一些语言则仅鼓励使用相对高级的原语(例如future / promises)。

在特定于语言的线程级别下,底层托管环境(通常是OS)进行多任务处理。操作系统级别的抢占式多任务用于实现(抢占式)多线程。在某些环境(如Windows NT)中,基本计划单元(任务)也是“线程”。为了通过上述线程的用户空间实现来区分它们,它们称为内核线程,其中“内核”是指操作系统的内核(但是,严格来说,对于Windows NT而言,这并不完全正确;“真正的”内核是NT执行)。内核线程并不总是1:1映射到用户空间线程,尽管1:1映射通常会减少大多数映射开销。由于内核线程非常繁重(涉及系统调用),因此无法创建/销毁/通信,用户空间中的绿色线程来克服开销问题,但以映射开销为代价。映射的选择取决于高级抽象中期望的编程范例。例如,当预期同时执行大量用户空间线程时(例如Erlang),1:1映射永远都不可行。

操作系统多任务处理的基础是处理器逻辑核心提供的ISA级多任务处理。这通常是程序员最底层的公共接口。在此级别下,可能存在SMT。这是一种由硬件实现的低级多线程的形式,但可以说,尽管有些通常只能由处理器制造商访问,但仍可以进行一些编程。请注意,硬件设计显然反映了并行性,但是也存在并发调度机制,以使内部硬件资源得到有效利用。

在上面提到的每个“线程”级别中,并发性和并行性都涉及到。尽管编程接口变化很大,但是所有接口在开始时都具有基本抽象所揭示的属性。


0

仅分享一个有助于突出区别的示例:

并行编程:假设您要实现合并排序算法。每次将问题分为两个子问题时,都可以有两个线程来解决。但是,为了执行合并步骤,您必须等待这两个线程完成,因为合并需要两个子解决方案。这种“强制等待”使它成为并行程序。

并发程序:假设您要压缩n个文本文件并为每个文本文件生成一个压缩文件。您可以有2个(最多n个)线程,每个线程都处理压缩文件的子集。每个线程完成后,就完成了,不必等待或执行任何其他操作。因此,由于以“任意顺序”以交错的方式执行不同的任务,因此程序是并发的,而不是并行的。

就像其他人提到的那样,每个并行程序都是并发的(实际上是并发的),但并非相反。


0

我将尝试以我自己的风格来解释它,它可能不是计算机术语,但它为您提供了总体思路。

让我们举个例子,说家务:打扫卫生,取出垃圾,修剪草坪等,我们也有3个人(线程)A,B,C来做

并发: 这三个人分别开始执行不同的任务,即

A --> cleaning dishes
B --> taking out trash 
C --> mowing the lawn 

在这里,任务的顺序是不确定的,响应取决于工作量

并行: 在这里,如果要提高生产率,我们可以将多个人分配给一个任务,例如,清洗碗碟时我们分配两个人,A用肥皂擦洗碗碟,B洗碗碟可以提高生产率。

洗碗:

A --> soaping the dishes
B --> washing the dishes

依此类推

希望这能给您一个想法!现在转到其他答案中解释的技术术语;)

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.