并行编程与并行编程之间的区别?


44

查看并发编程时,通常使用两个术语,即并发和并行。

一些编程语言特别要求支持并行编程,例如Java

这是否意味着并行和并行编程实际上是不同的?


10
是的,并发编程和并行编程是不同的。例如,您可以通过上下文切换在同一内核上同时执行两个线程(或进程)。当两个线程(或进程)在两个不同的内核(或处理器)上执行时,您具有并行性。因此,在前一种情况下(并发),并行性只是“虚拟的”,而在后一种情况下,您具有真正的并行性。因此,每个并行程序都是并发的,但是反过来并不一定成立。
Massimo Cafaro 2014年

1
小心点 您可以通过语言支持(即使用新的结构扩展语言)或使用低级方法(例如,在MPI和OpenMP中使用库)来获得相同的结果。无论如何,对于当前的多核处理器和具有SMP支持的操作系统,如果OS在不同的内核上调度程序的执行线程,则如果在旧的单核处理器上运行将并发运行的程序可以并行执行。因此,如今的区别有点“模糊”。
Massimo Cafaro 2014年

3
您使用的光延迟速度恒定。并发时,您假装光等待时间为一个时钟周期。并行地,您假设一台服务器在隔壁,而在分布式中,您假设一台服务器在火星上。


1
罗伯特·哈珀(Robert Harper)在两篇博客文章中讨论了该问题,您可能要检查一下它们:“并行不是并行”“并行与并行,已复习”
罗勒

Answers:


26

从并发(管理对共享资源的访问)中区分并行性(使用额外的计算单元在单位时间内完成更多工作)。首先教并行性,因为它更容易并且有助于建立非顺序的心态。

摘自Dan Grossman的“共享内存并行性和并发性的Sophomoric *简介”(2013年11月16日版本)


21

除了Nish的答案外,让我推荐Simon Marlow关于Haskell中的并行和并发编程的书或他的简短教程。它们从Haskell的角度回答了您的第一个问题,因此它们可能更适合于理论上有偏见的读者(Haskell是一种纯函数式的惰性编程语言,比其他语言更接近于数学)。

从那里报价:

在许多领域,并行和并发是同义词。在编程中则不是这样,在编程中它们用来描述根本不同的概念。

并行程序是一种使用多个计算硬件(例如,多个处理器内核)以便更快速地执行计算的程序。计算的不同部分被委派给同时(并行)执行的不同处理器,因此与顺序执行计算相比,可以更早地交付结果。

相反,并发是一种程序结构技术,其中有多个控制线程。通常,控制线程“同时”执行;即,用户看到了他们的效果交错。它们是否实际上同时执行是实现细节。并发程序可以通过交错执行在单个处理器上执行,也可以在多个物理处理器上执行。

我建议阅读本教程的其余内容(第4页),但让我引用本节的其余部分,因为它将编程范例与程序的定量和定性特征(例如效率,模块化和确定性)联系在一起。

尽管并行编程仅涉及效率,但并行编程则涉及构建需要与多个独立外部代理(例如,用户,数据库服务器和某些外部客户端)交互的程序。并发允许将此类程序模块化。与用户交互的线程不同于与数据库交互的线程。在没有并发的情况下,此类程序必须使用事件循环和回调编写---实际上,即使并发可用,事件循环和回调也经常被使用,因为在许多语言中,并发要么太昂贵,要么太难采用。

“控制线程”的概念在纯功能程序中没有意义,因为没有要观察的效果,并且评估顺序也没有关系。因此,并发是有效代码的一种结构化技术。在Haskell中,这意味着IO monad中的代码。

相关的区别是确定性编程模型和非确定性编程模型。确定性编程模型是其中每个程序只能给出一个结果的模型,而非确定性编程模型则根据执行的某些方面允许可能具有不同结果的程序。并发编程模型不一定是确定性的,因为它们必须与在无法预测的时间导致事件的外部代理进行交互。非确定性有一些明显的缺点,但是:程序变得很难测试和推理。

对于并行编程,我们将尽可能使用确定性编程模型。由于目标只是为了更快地找到答案,因此我们宁愿不要使我们的程序在此过程中难以调试。确定性并行编程是两全其美的方法:可以在顺序程序上执行测试,调试和推理,但是添加处理器后程序的运行速度更快。实际上,大多数计算机处理器本身都以流水线和多个执行单元的形式实现确定性并行性。

尽管可以使用并发进行并行编程,但这通常是一个糟糕的选择,因为并发可以简化确定性。在Haskell中,并行编程模型是确定性的。但是,重要的是要注意,确定性编程模型不足以表示所有种类的并行算法。有些算法依赖于内部不确定性,尤其是涉及搜索解决方案空间的问题。在Haskell中,此类算法只能使用并发表示。


20

并行性和并行性在它们解决和引起的问题上有所不同,但它们不是独立的。

并发

执行两个任务同时意味着两个任务的各个步骤以交错的方式被执行。如果不考虑并行性,则可以假定在任何时间点仅执行一条语句,但是(先验)不能保证执行下一步的任务。

这在某些方面很有用:

  • 在一个程序中对独立任务进行更清晰的编程。
  • 允许在计算时处理IO(例如在GUI中)。
  • 允许一次执行多个程序(在OS级别上并发)。

主要挑战包括:

  • 保持数据一致性。
  • 避免死锁活锁
  • 确定并发进程的精确语义。
  • 确定可确保正确性的静态属性。

平行性

执行两个任务并行意味着语句执行在同一时间。这主要用于:

  • 通过并行执行程序(例如在多核系统上)提高系统吞吐量。
  • 通过一次利用多个CPU来改善单个程序的运行时间。
  • 在许多机器(例如分布式数据库)上使用IO。

关键挑战包括:

  • 分区问题允许并开发可以采用并行性的算法。
  • 最小化计算单元之间的依赖性和通信。
  • 并发带来的所有问题:至少从内存的角度来看,由于内存访问的序列化,并行程序看起来像并发程序。
  • 处理次优的硬件支持。

另请参阅此问题以区分并行计算和分布式计算。


4

稍微理想化的答案,也许...

  • 并发是程序编写方式的一个属性。如果程序是使用派生/联接,锁,事务,原子比较和交换操作等构造编写的,则它是并发的。

  • 并行性是程序执行方式的一个属性。如果一个程序同时在一个以上的计算单元上执行,那么它正在并行执行。


1

对此有很多答案,但这可能会造成混淆。我喜欢这样想,也许有帮助吗?:

并发编程是不关心执行顺序的代码。Java是并发编程的一种糟糕的语言,但是有一些库和框架可以提供帮助。JavaScript是并发编程的优秀语言,当您要编写并发的内容时(例如,如果要强制执行顺序),通常很难做到这一点。并发编程非常适合事件驱动的编程(事件的执行顺序由事件侦听器确定,例如单击按钮或在框中键入内容时在浏览器中运行的代码)。

一个示例包括创建一百个HTTP请求。在NodeJS中,最简单的解决方案是使用回调方法一次打开所有100个请求,然后当响应返回时,每次都执行一个方法。那是并发编程。在Ruby中,最简单(最常见)的解决方案是打开一个请求并处理响应,打开下一个请求并处理响应,等等。对于许多请求,NodeJS易于及时完成,尽管您必须注意避免重击服务器或最大化出站连接(容易犯错误)。您可以以并发方式编写Ruby,但这并不是大多数Ruby代码的编写方式,这样做会有些痛苦。

并行编程是可以在多个线程或进程中同时运行的代码。这使您可以通过在多个CPU(通常包括多台计算机,如Akka之类的设备)上运行代码来优化性能。因为NodeJS不是多线程的并且没有并行执行,所以您不必担心编写线程安全的代码(而且我见过的大多数JavaScript代码都不是线程安全的)。在Java中,即使该语言没有使并发编程成为正常模式,但并行编程是内置的,并且您经常不得不担心线程安全性。如果您使用Java编写网站,则通常会在一个容器中运行该容器,该容器在同一内存中的单独线程中运行每个请求,


以上某些内容取决于您所讨论的范围和界限。我在网站上工作。我看到的大多数Java代码都不是并发编程。当然,如果您放大得足够多,那么客户要求的输入顺序并不重要,但是如果您进一步放大,则执行代码的顺序将由代码决定。但是编写了代码,以便请求可以与许多必须是线程安全的共享对象并行执行。

同时,我看到的大多数JavaScript代码都是并发的:它的编写顺序在许多级别上都不重要。但这并不是为了支持共享内存中的并行执行而编写的。当然,您可以在多个进程中并行执行相同的代码,但是不会共享对象,因此从任何有意义的意义上讲,它都不是并行编程。

对于其他阅读,我真的很喜欢此问题的最高答案中的插图:https : //www.quora.com/What-are-the-differences-between-parallel-concurrent-and-asynchronous-programming

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.