并发和并行之间的区别是什么?
当前回答
我将提供一个与这里的一些流行答案有点冲突的答案。在我看来,并发是一个包含并行性的通用术语。并发适用于不同任务或工作单元在时间上重叠的任何情况。并行性更具体地适用于在同一物理时间评估/执行不同工作单元的情况。并行性存在的原因是加速了可以从多个物理计算资源中受益的软件。适用于并发的另一个主要概念是交互性。当从外部世界可以观察到任务的重叠时,互动性适用。交互性存在的原因是使软件能够响应真实世界的实体,如用户、网络对等体、硬件外围设备等。
并行性和交互性几乎完全独立于并发性。对于一个特定的项目,开发人员可能会关心其中之一,或者两者都不关心。它们往往会被混淆,尤其是因为线程这一令人厌恶的东西给了一个相当方便的原语来实现两者。
关于并行性的更多细节:
并行性存在于非常小的规模(例如处理器中的指令级并行性)、中等规模(例如多核处理器)和大型规模(例如高性能计算集群)。近年来,由于多核处理器的发展,软件开发人员暴露更多线程级并行性的压力越来越大。平行性与依赖性密切相关。依赖性限制了并行性的实现程度;如果一个任务依赖于另一个任务,则两个任务不能并行执行(忽略推测)。
程序员可以使用许多模式和框架来表达并行性:管道、任务池、数据结构上的聚合操作(“并行数组”)。
关于互动性的更多细节:
最基本和最常见的交互方式是使用事件(即事件循环和处理程序/回调)。对于简单的任务,事件是很好的。尝试使用事件执行更复杂的任务会导致堆栈撕裂(也称为回调地狱;也称为控制反转)。当你厌倦了事件时,你可以尝试更奇特的东西,比如生成器、协程(又称Async/Await)或合作线程。
出于对可靠软件的热爱,如果你想要的是交互性,请不要使用线程。
曲线几何非线性
我不喜欢Rob Pike的“并发不是并行;它更好”口号。并发既不比并行好,也不比并行差。并发性包括交互性,不能以更好/更差的方式与并行性进行比较。这就像说“控制流比数据更好”。
其他回答
为什么存在困惑
由于这两个词的字典含义几乎相同,因此存在混淆:
并发:同时存在、发生或完成(dictionary.com)平行:非常相似,经常同时发生(梅里亚姆·韦伯斯特)。
然而,它们在计算机科学和编程中的使用方式却截然不同。以下是我的解释:
并发性:可中断性并行性:独立性
那么,我所说的上述定义是什么意思?
我将用一个现实世界的类比来澄清。假设你必须在一天内完成两项非常重要的任务:
获取护照完成演示文稿
现在,问题是任务1需要你去一个极其官僚的政府办公室,这让你排队等候4个小时才能拿到护照。同时,任务2是你的办公室需要的,这是一项关键任务。两者都必须在特定的一天完成。
案例1:顺序执行
通常,你会开车去护照办公室2个小时,排队4个小时,完成任务,开车回去两个小时,回家,再睡5个小时,然后完成演讲。
案例2:并发执行
但你很聪明。你要提前计划。你随身携带一台笔记本电脑,在排队等候的时候,你开始做你的演讲。这样,一旦你回到家里,你只需要额外工作1小时而不是5小时。
在这种情况下,这两项任务都是由您完成的,只是零碎的。您在排队等候时中断了护照任务,并进行了演示。当您的号码被呼叫时,您中断了演示任务,并切换到护照任务。由于这两项任务的可中断性,节省时间基本上是可能的。
并发性,IMO,可以理解为ACID中的“隔离”属性。如果子事务可以以任何交错的方式执行,那么两个数据库事务被认为是隔离的,并且如果两个任务是顺序完成的,那么最终结果是相同的。记住,对于护照和演示任务,你是唯一的刽子手。
案例3:并行执行
现在,既然你是个聪明的家伙,你显然是个更高级的人,而且你有一个助手。所以,在你开始护照任务之前,你给他打电话,告诉他准备演示文稿的初稿。你花了一整天的时间完成护照任务,回来看看你的邮件,然后找到了演示稿。他做得很扎实,再过两个小时就完成了一些编辑。
现在,你的助手和你一样聪明,他能够独立工作,而不需要经常要求你澄清。因此,由于任务的独立性,它们由两个不同的执行者同时执行。
还和我在一起吗?好吧
案例4:并行但不并行
还记得你的护照任务吗,你必须在排队等候的地方?因为这是你的护照,你的助手不能排队等你。因此,护照任务具有可中断性(您可以在排队等候时停止它,稍后当您的号码被呼叫时再继续),但没有独立性(您的助手不能代替您等待)。
案例5:并行但不并发
假设政府办公室有一个安全检查来进入办公场所。在这里,您必须移除所有电子设备并将其提交给官员,他们只会在您完成任务后归还您的设备。
在这种情况下,护照任务既不可独立,也不可中断。即使你在排队,你也不能做其他事情,因为你没有必要的设备。
同样地,假设演示是高度数学化的,你需要100%集中至少5小时。你不能在排队等候护照任务时做这件事,即使你随身携带笔记本电脑。
在这种情况下,演示任务是独立的(您或您的助手可以投入5小时的精力),但不能中断。
案例6:并发和并行执行
现在,假设除了指派你的助手参加演示外,你还随身携带一台笔记本电脑来完成护照任务。在排队等候时,您会看到您的助手在共享幻灯片组中创建了前10张幻灯片。你对他的工作发表了评论,并做了一些更正。后来,当你回到家时,你只需要15分钟,而不是2个小时来完成草稿。
这是可能的,因为演示任务具有独立性(你们中的任何一个都可以完成)和可中断性(你们可以停止它,稍后再继续它)。因此,您同时执行了两个任务,并并行执行了演示任务。
比方说,除了过于官僚之外,政府办公室也是腐败的。因此,你可以出示你的身份证,输入它,开始排队等待你的号码被呼叫,贿赂一名警卫和其他人来保持你在队伍中的位置,偷偷溜出去,在号码被呼叫之前回来,然后自己继续等待。
在这种情况下,您可以同时并行执行护照和演示任务。你可以偷偷溜出去,你的位置由你的助手掌握。然后你们两个都可以进行演示等。
回到计算机科学
在计算世界中,以下是每种情况的典型场景:
情况1:中断处理。情况2:只有一个处理器,但由于I/O,所有正在执行的任务都有等待时间。案例3:当我们谈论map reduce或hadoop集群时经常会看到。案例4:我认为案例4很罕见。任务是并发但不是并行的,这是不常见的。但这可能会发生。例如,假设您的任务需要访问只能通过处理器1访问的特殊计算芯片。因此,即使处理器-2是空闲的,而处理器-1正在执行其他任务,特殊计算任务也不能在处理器-2上继续。病例5:同样罕见,但不像病例4那样罕见。非并发代码可以是受互斥锁保护的关键区域。一旦启动,它必须执行到完成。然而,两个不同的关键区域可以在两个不同处理器上同时进行。案例6:IMO,关于并行或并发编程的大多数讨论基本上都在讨论案例6。这是并行和并发执行的混合和匹配。
并发和Go
如果你明白Rob Pike为什么说并发性更好,你必须明白原因是什么。你有一个非常长的任务,其中有多个等待期,你需要等待一些外部操作,如文件读取、网络下载。在他的演讲中,他所说的是,“把这一长串的任务分解,这样你就可以在等待的时候做一些有用的事情。”这就是为什么他会和各种各样的地鼠谈论不同的组织。
现在,围棋的力量来自于使用围棋关键词和频道,让这一突破变得非常容易。此外,在运行时中有很好的底层支持来调度这些goroutine。
但本质上,并发比并行好吗?
苹果比橙子好吗?
简单示例:
并发是:“两个队列访问一台ATM机”
并行是:“两个队列和两台ATM机”
我认为在这个问题上有两种不同的观点导致了混淆:程序员的观点(并发/并行编程)与计算机/操作系统的观点(并行/并行执行)。
这里回答了计算机的观点。
程序员的观点:
并发编程:程序员编写代码时知道代码将由多个线程执行,无论出于何种原因。原因可能是:在等待I/O时更好地利用CPU,通过不同线程处理Web请求,通过在独立于主线程的线程中运行计算,运行周期性后台任务,使GUI做出响应。程序员必须应用互斥构造、锁定/解锁、等待条件/信号、处理死锁等。多个线程可以在单个处理器/内核上运行(从计算机的角度来看是并发的),也可以在多个内核上运行。
并行编程:程序员知道程序将在具有多个处理器/内核的计算机上运行,并希望利用多个内核。程序员将CPU密集型计算划分为多个子任务,在一个线程中运行每个子任务,一旦线程完成,其结果将合并为总结果(分而治之)。例如,将一些矩阵处理代码划分为并行处理矩阵部分的任务。每个核心将使用子任务执行一个线程(如果线程数大于内核数,则同时执行多个线程)。程序员也必须在这里应用并发的编程构造,但她也关注将任务划分为子任务并合并结果。例如,在Java中,程序员可以使用ParallelStreams来分割数据并自动合并结果。如果程序员知道程序将在单核处理器上执行,那么将CPU密集型任务拆分为多个线程是没有好处的。摘自Doug Leah的《Java并发编程:设计原则和模式》,1999年第2版,第343页:
并行程序专门设计为利用多个CPU来解决计算密集型问题。
派克的“并发”概念是一个有意的设计和实现决策。具有并发能力的程序设计可能表现出行为上的“并行性”;这取决于运行时环境。
你不希望一个不是为并发而设计的程序表现出并行性。:-)但就相关因素(功耗、性能等)而言,这是一个净收益,您需要最大程度的并发设计,以便主机系统可以在可能的情况下并行执行。
派克的Go编程语言将这一点发挥到了极致:他的函数都是可以同时正确运行的线程,也就是说,如果系统有能力,调用函数总是会创建一个与调用者并行运行的线程。一个拥有数百甚至数千个线程的应用程序在他的世界中是非常普通的。(我不是围棋专家,这只是我的看法。)
我将提供一个与这里的一些流行答案有点冲突的答案。在我看来,并发是一个包含并行性的通用术语。并发适用于不同任务或工作单元在时间上重叠的任何情况。并行性更具体地适用于在同一物理时间评估/执行不同工作单元的情况。并行性存在的原因是加速了可以从多个物理计算资源中受益的软件。适用于并发的另一个主要概念是交互性。当从外部世界可以观察到任务的重叠时,互动性适用。交互性存在的原因是使软件能够响应真实世界的实体,如用户、网络对等体、硬件外围设备等。
并行性和交互性几乎完全独立于并发性。对于一个特定的项目,开发人员可能会关心其中之一,或者两者都不关心。它们往往会被混淆,尤其是因为线程这一令人厌恶的东西给了一个相当方便的原语来实现两者。
关于并行性的更多细节:
并行性存在于非常小的规模(例如处理器中的指令级并行性)、中等规模(例如多核处理器)和大型规模(例如高性能计算集群)。近年来,由于多核处理器的发展,软件开发人员暴露更多线程级并行性的压力越来越大。平行性与依赖性密切相关。依赖性限制了并行性的实现程度;如果一个任务依赖于另一个任务,则两个任务不能并行执行(忽略推测)。
程序员可以使用许多模式和框架来表达并行性:管道、任务池、数据结构上的聚合操作(“并行数组”)。
关于互动性的更多细节:
最基本和最常见的交互方式是使用事件(即事件循环和处理程序/回调)。对于简单的任务,事件是很好的。尝试使用事件执行更复杂的任务会导致堆栈撕裂(也称为回调地狱;也称为控制反转)。当你厌倦了事件时,你可以尝试更奇特的东西,比如生成器、协程(又称Async/Await)或合作线程。
出于对可靠软件的热爱,如果你想要的是交互性,请不要使用线程。
曲线几何非线性
我不喜欢Rob Pike的“并发不是并行;它更好”口号。并发既不比并行好,也不比并行差。并发性包括交互性,不能以更好/更差的方式与并行性进行比较。这就像说“控制流比数据更好”。