ChatGPT解决这个技术问题 Extra ChatGPT

如何阐明异步和并行编程之间的区别?

许多平台提倡异步和并行作为提高响应能力的手段。我一般理解其中的区别,但经常发现在我自己和其他人的脑海中很难表达清楚。

我是一名工作日程序员,经常使用异步和回调。并行性感觉很奇特。

但我觉得它们很容易混淆,尤其是在语言设计层面。希望清楚地描述它们之间的关系(或不相关),以及最适合应用的程序类别。

我写了一篇关于异步和并行编程之间关系的博文 - anat-async.blogspot.com/2018/08/…
并行性是指事情同时发生。异步性是指您不必费心等待操作的结果继续进行。你只是去睡觉,在稍后的某个时间点,结果来了,敲响你的铃,你醒来并从那里继续。异步执行可以完美地仅在一个线程中串行发生。 (这几乎就是 javascript 所做的)

C
CodeMan

当您异步运行某些内容时,这意味着它是非阻塞的,您无需等待它完成即可执行它并继续执行其他操作。并行意味着同时并行运行多个事物。当您可以将任务分成独立的工作时,并行性会很好地工作。

以渲染 3D 动画的帧为例。渲染动画需要很长时间,因此如果您要从动画编辑软件中启动该渲染,您将确保它异步运行,因此它不会锁定您的 UI,您可以继续做其他事情。现在,该动画的每一帧也可以被视为一个单独的任务。如果我们有多个 CPU/Core 或多台机器可用,我们可以并行渲染多个帧以加快整体工作负载。


让我看看我能不能得到这个。渲染不同帧的并行任务应该分布在多个 CPU/内核上。这与任务完成的时间无关,或者该任务是否阻塞了其他东西。这只是意味着一堆 CPU 将一起完成它并使结果可用,就好像它在一个超快的 CPU 上运行一样。正确的?
“渲染动画需要很长时间,因此如果您要从动画编辑软件中启动该渲染,您将确保 (...)”。什么?
对于 3D 动画部分:首先,您永远不会在 CPU 上运行生成帧的 3D 图形程序 - 任何理智的人都会立即建议使用 GPU。其次,如果我们这样做(非常不鼓励),我们将使用计时器来测量我们可以渲染多少帧,否则我们最终可能只会构建一堆未完成的渲染调用任务。但是您的观点对于大多数基于每个用户输入事件进行渲染的 2D 渲染应用程序是完全有效的。
异步和非阻塞是不同的范例。
i
igon

我认为主要区别在于并发性和并行性之间。

异步和回调通常是一种表达并发的方式(工具或机制),即一组可能相互交谈和共享资源的实体。在异步或回调通信的情况下是隐式的,而资源共享是可选的(考虑在远程机器中计算结果的 RMI)。正如正确指出的那样,这通常是在考虑响应性的情况下完成的;不要等待长延迟事件。

并行编程通常将吞吐量作为主要目标,而延迟(即单个元素的完成时间)可能比等效的顺序编程更差。

为了更好地理解并发性和并行性之间的区别,我将引用 Daniele Varacca 的并发概率模型,这是并发理论的一组很好的注释:

当计算模型能够将系统表示为由独立的自治组件组成并可能相互通信时,它就是并发模型。并发的概念不应与并行的概念相混淆。并行计算通常涉及在多个处理器之间分配工作的中央控制。在并发方面,我们强调组件的独立性,以及它们相互通信的事实。平行主义就像古埃及,法老决定,奴隶工作。并发就像现代意大利,每个人都想做什么就做什么,都使用手机。

总之,并行编程在某种程度上是并发的一种特殊情况,其中不同的实体协作以获得高性能和吞吐量(通常)。

Async 和 Callbacks 只是一种允许程序员表达并发的机制。考虑到众所周知的并行编程设计模式(例如 master/worker 或 map/reduce)是由使用此类较低级别机制(异步)的框架实现的,以实现更复杂的集中式交互。


J
JustATrick

这篇文章解释得很好:http://urda.cc/blog/2010/10/04/asynchronous-versus-parallel-programming

它有关于异步编程的内容:

异步调用用于防止应用程序中的“阻塞”。 [这样的] 调用将在已经存在的线程(例如 I/O 线程)中分拆并在可能的情况下执行其任务。

这是关于并行编程的:

在并行编程中,您仍然会分解工作或任务,但主要区别在于您为每个工作块启动新线程

总而言之:

异步调用将使用系统已在使用的线程,并行编程要求开发人员分解所需的工作、启动和拆卸线程。


这篇文章>这里的所有答案(当然除了这个!)
感谢您的链接。所以......一般来说,在从 UI 到服务器(或从客户端到 Web 服务)通信时使用异步调用。在服务器或 Web 服务端以及您的业务层中使用并行线程。
我必须不同意,是否启动新线程无关紧要。最简单的 #pragma omp parallel for 通常使用线程池:OpenMP 在启动时为每个核心旋转一个线程,然后将它们重用于每个并行区域。我想说这更像是“所有异步任务都可以在同一个线程上运行”,不仅避免启动新线程,而且完全避免使用线程。例如,Javascript 是完全单线程但完全异步的。
A
Andrew Cooper

我的基本理解是:

异步编程解决了在执行任何其他操作之前等待昂贵的操作完成的问题。如果您可以在等待操作完成的同时完成其他工作,那么这是一件好事。示例:在您从 Web 服务检索更多数据时保持 UI 运行。

并行编程是相关的,但更关心将大型任务分解为可以同时计算的较小块。然后可以组合较小块的结果以产生整体结果。示例:光线追踪,其中单个像素的颜色基本上是独立的。

它可能比这更复杂,但我认为这是基本的区别。


这是很好的说法,但它是完全错误的。与异步性一样,并行性也允许控制流继续进行,而无需等待动作完成。主要区别在于并行性取决于硬件。
s
serkan

异步:在其他地方自己做,完成后通知我(回调)。到时候我可以继续做我的事情了。

https://i.stack.imgur.com/3sSch.jpg

并行:雇用尽可能多的人(线程)并将工作分配给他们以更快地完成,并在完成时告诉我(回调)。到时候我可能会继续做我的其他事情。

https://i.stack.imgur.com/3PqVx.jpg

主要区别是并行性主要取决于硬件。


L
Leonard H. Martin

我倾向于认为这些术语的区别:

异步:走开,做这个任务,当你完成后回来告诉我并带来结果。在此期间,我将继续处理其他事情。

平行:我要你做这个任务。如果它使事情变得更容易,请一些人帮忙。不过这很紧急,所以我会在这里等你拿结果回来。在你回来之前,我什么也做不了。

当然,异步任务可能会利用并行性,但区别 - 至少在我看来 - 是您是否在执行操作时继续处理其他事情,或者您是否完全停止一切直到结果出来。


R
Richard

这是执行顺序的问题。

如果 A 与 B 异步,那么我无法事先预测 A 的子部分何时相对于 B 的子部分发生。

如果 A 与 B 并行,则 A 中的事情与 B 中的事情同时发生。但是,仍然可以定义执行顺序。

也许困难在于异步这个词是模棱两可的。

当我告诉管家去商店买更多的酒和奶酪时,我执行了一个异步任务,然后忘了他,继续写我的小说,直到他再次敲门。平行现象在这里发生,但管家和我从事的是根本不同的任务和不同的社会阶层,所以我们在这里不贴这个标签。

当她们每个人都在清洗不同的窗户时,我的女仆团队正在并行工作。

我的赛车支持团队是异步并行的,因为每个团队都在不同的轮胎上工作,他们在工作时不需要相互沟通或管理共享资源。

我的足球(又名足球)团队进行并行工作,因为每个球员独立处理有关场地的信息并在场地上移动,但他们并不是完全异步的,因为他们必须沟通并响应其他人的沟通。

我的军乐队也是平行的,因为每个演奏者都阅读音乐并控制他们的乐器,但他们是高度同步的:他们彼此演奏和进行曲。

凸轮式加特林枪可以被认为是并行的,但一切都是 100% 同步的,所以就好像一个过程正在向前推进。


J
Jameel Moideen

为什么是异步的?

随着今天的应用程序越来越多的连接和潜在的长时间运行的任务或阻塞操作,如网络 I/O 或数据库操作。因此,通过在后台启动这些操作并返回用户界面来隐藏这些操作的延迟非常重要尽快。这里异步进入图片,响应性。

为什么要并行编程?

随着今天的数据集越来越大,计算越来越复杂。因此,减少这些 CPU 密集型操作的执行时间非常重要,在这种情况下,通过将工作负载分成块然后同时执行这些块。我们可以称之为“并行”。显然,它将为我们的应用程序提供高性能。


P
Pang

异步 假设您是客户的联系人,您需要做出响应,即您需要在被询问时共享状态、操作的复杂性、所需的资源等。现在您需要完成一项耗时的操作,因此您无法完成此操作,因为您需要 24/7 全天候响应客户端。因此,您将耗时的操作委托给其他人,以便您能够做出响应。这是异步的。

并行编程 假设您有一个任务要从一个文本文件中读取 100 行,而读取一行需要 1 秒。因此,您需要 100 秒来读取文本文件。现在您担心客户端必须等待 100 秒才能完成操作。因此,您再创建 9 个克隆,并让每个克隆从文本文件中读取 10 行。现在读取 100 行只需要 10 秒。因此你有更好的表现。

综上所述,异步编码是为了实现响应性,并行编程是为了提高性能。


A
Aditya Bokade

异步:在后台运行方法或任务,不阻塞。不一定要在单独的线程上运行。使用上下文切换/时间调度。

并行任务:每个任务并行运行。不使用上下文切换/时间调度。


P
Pang

我来到这里对这两个概念相当满意,但对它们有一些不清楚的地方。

在阅读了一些答案后,我认为我有一个正确且有用的比喻来描述差异。

如果您认为您的各个代码行是独立但有序的扑克牌(如果我在解释老式打孔卡的工作原理,请阻止我),那么对于编写的每个单独的程序,您将拥有一组独特的卡片(不要复制和粘贴!)正常运行代码和异步运行代码时的正常运行情况之间的区别取决于您是否关心。

当您运行代码时,您将一组单一操作(您的编译器或解释器将您的“更高”级别代码分解成)交给操作系统以传递给处理器。使用一个处理器,任何时候只能执行一行代码。因此,为了实现同时运行多个进程的假象,操作系统使用了一种技术,在该技术中,它一次只向处理器发送来自给定进程的几行代码,并根据它所看到的方式在所有进程之间切换合身。结果是多个进程在似乎同时向最终用户显示进度。

对于我们的比喻,这种关系是操作系统总是在将卡片发送到处理器之前对其进行洗牌。如果你的牌堆不依赖于另一个牌堆,你不会注意到你的牌堆停止被选中,而另一个牌堆被激活。所以,如果你不在乎,那也没关系。

但是,如果您确实关心(例如,有多个进程 - 或一堆卡片 - 确实相互依赖),那么操作系统的洗牌将搞砸您的结果。

编写异步代码需要处理执行顺序之间的依赖关系,无论该顺序最终是什么。这就是使用“回调”之类的结构的原因。他们对处理器说,“接下来要做的就是告诉另一个堆栈我们做了什么”。通过使用此类工具,您可以确保其他堆栈在允许操作系统运行任何更多指令之前得到通知。 (“If called_back == false: send(no_operation)” - 不确定这是否真的是它的实现方式,但从逻辑上讲,我认为它是一致的。)

对于并行进程,不同之处在于您有两个互不关心的堆栈和两个工作人员来处理它们。归根结底,您可能需要合并来自两个堆栈的结果,这将是一个同步问题,但对于执行,您不再关心。

不确定这是否有帮助,但我总是发现多种解释很有帮助。另外,请注意异步执行不限于单个计算机及其处理器。一般来说,它处理时间,或者(甚至更一般地说)事件的顺序。因此,如果您将依赖堆栈 A 发送到网络节点 X 并将其耦合堆栈 B 发送到 Y,则正确的异步代码应该能够解释这种情况,就好像它在您的笔记本电脑上本地运行一样。


P
Pang

一般来说,每次只有两种方法可以做不止一件事。一个是异步的,另一个是并行的。

从高层看,像流行的服务器 NGINX 和著名的 Python 库 Tornado 一样,它们都充分利用了异步范式,即单线程服务器可以同时服务数千个客户端(一些 IOloop 和回调)。使用可以实现异步编程范式的ECF(异常控制跟随)。所以异步有时并不能真正同时做事情,但是一些 io 绑定的工作,异步可以真正提升性能。

并行范式总是指多线程和多处理。这样可以充分利用多核处理器,真正做到同时做事。


V
Vyshnav Ramesh Thrissur

以上所有答案的摘要

并行计算:

▪ 解决吞吐量问题。担心将大任务分解成更小的块

▪ 与机器相关(需要多台机器/核心/cpu/处理器),例如:主从,map reduce。

并行计算通常涉及一个中央控制,它在多个处理器之间分配工作

异步:

▪ 解决延迟问题,即“等待”昂贵的操作完成后,您才能执行任何其他操作的问题

▪ 与线程相关(需要多线程)

线程(使用 Thread、Runnable、Executor)是在 Java 中执行异步操作的一种基本方式