一个“制作午餐”的故事,帮助你理解并发和并行(上)

关注公众号,后台回复“python资料”,获取python资料大礼包。

现在思路实验室公众号发布的文章太多了,为了能够最快地找到自己关心的内容,请大家关注公众号,在公众号后台菜单栏进行分类选择,很快很方便,妈妈再也不用担心我的学习!

导读

这是一个关于“午餐时间”的小故事,用于阐述 threading、asyncio、multiprocessing、cloud functions 等内容。为了方便阅读并理解文章的内容,全文分上、下两篇,上篇主要讲解并发,下篇重点讲解并行。

介绍

我们将会讲述一个故事,来解释 python 中并发与并行的不同之处。

在这个故事中,我们将看到一个单人进行多任务处理的场景(类似并发),以及一个多人分别处理自己任务的场景(类似并行);我们会站在餐厅的角度观察这些场景的实际效果,并观察它们如何快速有效地为顾客服务;然后我们将在 Python 中实现这些“餐厅”;最后,我们会比较这两种不同的并发选项,并解释如何择机使用它们。

解释的内容包含:

并发和并行之间有什么区别?

不同的并发选项以及比较它们的方式,包括 threading、asyncio、multiprocessing、cloud functions 等

它们各自的优缺点

使用流程图来介绍选择并发选项的思路

什么是并发和并行?

让我们从定义开始:

如果一个系统可以同时支持两个或多个正在进行中的操作,则称该系统是并发的。

如果一个系统可以支持同时执行两个或多个动作,则称该系统是并行的。

这些定义之间的关键概念和区别在于“进行中”这一短语。— 并发的艺术[1]

如果你被绕晕了,现在让我们直接通过制作午餐的故事来讲解。

在午餐时间,你拐进了一条之前从未注意到的街道。这里有两种可供选择的食物来源:一个叫做 Concurrent Burgers 的市场摊位和一个叫做 Parallel Salads 的商店。

两者看起来都很美味,但都在排长队,所以你想知道哪一个会先为你服务。

Concurrent Burgers 由一位手臂上有蟒蛇纹身的中年女士经营,她在工作时开怀大笑。她正在执行以下任务:

接单

翻转汉堡肉饼

用沙拉、肉饼和调味品填满面包,然后完成订单

她在每个任务之间无缝切换:有一刻,她正在检查烤架上的肉饼并将煮熟的取出,下一刻她正在接受订单,再下一刻如果有任何肉饼已经准备好,她会制作一个汉堡并完成这笔订单。

Parallel Salads 配备了许多相同的人,他们在工作时面带微笑并礼貌地交谈。他们每个人都为一位顾客制作沙拉。他们接受订单,将所有原料加入一个新鲜的碗中,浇上调味汁,尽情地混合,在一个容器中装满一份健康的沙拉,然后丢掉碗。与此同时,另一个克隆人拿起脏碗并清洗它们。

两家的主要区别在于员工数量和执行任务的方式:

Concurrent Burgers “同时”执行多个任务,并且仅有一个工作人员在它们之间切换。

Parallel Salads 有多个同时进行的任务,并且有多个工人每次只负责该任务中的一部分。

你注意到:两家餐厅都以相同的速度为顾客提供服务。Concurrent Burgers 中的女士同时制作多个汉堡,并且受到她的小烤架输出熟肉饼的速度的限制。

Parallel Salads 雇用多名男子一次制作一份沙拉,并且受到将制作单份沙拉的材料放在一起所需时间长度的限制。

你很快意识到 Concurrent Burgers 受 I/O 限制,而 Parallel Salads 受 CPU 限制:

I/O 密集型意味着程序受 I/O 子系统的限制,在计算机术语中意味着从磁盘读取或执行网络请求。在 Concurrent Burgers 中,它指的是肉饼烹饪;

CPU 密集型意味着程序受 CPU 速度的限制。如果 CPU 运行得更快,程序就会运行得更快。在 Parallel Salads 中,它是制作沙拉的人的处理速度。

在一个固执己见的朋友打断你并邀请你加入他们的队列之前,你无法做出决定,你可能会在相同的状态下保持五分钟的困惑。

需要注意的是,Parallel Salads 是并发,也是并行的,因为“两个或多个操作同时进行”。并行处理是并发处理的一个子集。

这两个商店为并发和并行任务之间的区别提供了一种直观的视角。下面我们将研究如何在 Python 中实现这两者。

可供使用的选项

Python 有两个可用于并发的选项:

threading

asyncio

同时它内置了这个库以实现并行性:

multiprocessing

在云上运行 Python 程序时,还有另一种并行选项:

cloud functions

实践并发

让我们看一下使用 threading 和 asyncio 的 Concurrent Burgers 的两种可能实现。在这两种情况下,都有一个工人接单、做肉饼和做汉堡。

对于 threading 和 asyncio,都只有一个处理器在运行,但它在需要执行的不同任务之间跳转。threading 和 asyncio 之间的区别在于如何切换任务。

在 threading 中,操作系统掌控不同的线程,并且会在任何时候中断它们并切换到不同的任务。程序本身无法控制这一点。这称为抢占式多任务处理,因为操作系统可以抢占您的线程以进行切换。在大多数编程语言中,线程并行运行,但在 Python 中,一次只允许执行一个。

使用 asyncio,则是由程序本身决定何时在任务之间切换。每个任务通过在准备切换时,放弃对当前任务的控制,来与其他任务合作。出于这个原因,它被称为‘协作多任务“:因为当每个任务无法再取得进展时,它必须通过放弃控制来进行合作。

使用 threading 实现 Concurrent Burgers

通过 threading,工作人员可以在执行期间随时切换任务。这名工人正在下订单时突然切换到检查馅饼或制作汉堡,然后又随时切换到其他任务之一。

让我们来看一下使用 threading 实现的 Concurrent Burgers:

接受订单、烹饪肉饼和制作汉堡的每一项任务都是一个无限循环,不断执行其动作。

在 中,我们在单独的线程中启动每个任务。我们可以为每个任务手动创建一个线程,但是有一个更好的接口,称为 ,它为我们提交给它的每个任务创建一个线程。

当使用多个线程时,我们必须确保一次只有一个线程在读取或写入任何状态。否则我们可能会遇到两个线程拿着同一个馅饼的情况,我们最终会遇到一个相当愤怒的顾客;这个问题被称为线程安全

为了避免这个问题,我们使用 来传递状态。在单个任务中,调用 get 时 会阻塞,直到有客户、订单或小馅饼准备好。操作系统不会尝试切换到任何被阻塞的线程,这为我们提供了一种安全切换状态的简单方法。只要将状态放入 线程不再使用它,那么获取状态的线程就知道它在使用时不会改变。

threading 的优点

I/O 不会阻塞其他任务的进行

出色的 Python 版本和库支持——如果它可以单线程运行,它很可能也可以多线程运行

threading 的缺点

由于系统线程之间切换的开销,比 asyncio 慢

非线程安全

对于像制作沙拉这样的 CPU 密集型问题(由于 Python 只允许一个线程同时运行)没有效果 -- 一个工人同时制作多个沙拉不会比他们一个接一个地制作沙拉更快,因为每份沙拉仍然需要同样的时间来制作。

使用 asyncio 实现 Concurrent Burgers

在 asyncio 中有一个事件循环来管理所有任务。任务可以处于多种不同的状态,但最重要的两个状态是就绪或等待。在每个循环中,事件循环都会检查:是否有任何处于等待状态的任务由于另一个任务完成而准备就绪。然后它选择一个就绪任务并运行它,直到任务完成或需要等待另一个任务,这通常是一个 I/O 操作,比如从磁盘读取或发出一个 http 请求。

有两个关键字涵盖了 asyncio 的大部分用途:async 和 await。

async 用于标记函数必须作为单独的任务运行。

await 创建一个新任务并放弃对事件循环的控制。它将任务置于等待状态,并在新任务完成时再次准备就绪。

让我们来看一下使用 asyncio 实现的 Concurrent Burgers:

接受订单、烹饪肉饼和制作汉堡的每一项任务都是用 声明的。在这些任务中,每次调用 时,worker 都会切换到一个新任务。会出现以下场景:

接单的时候

当即将与下一位客户交谈时

将订单添加到订单队列时

做馅饼的时候

当所有的馅饼都检查完后

做汉堡的时候

在等待熟肉饼时

等待订单时

当找到顾客给他们汉堡时

最后一个难题是在 中,它调用 来安排所有任务由事件循环运行,在这种情况下,事件循环就是我们的工作人员。

正如我们确切地知道,任务切换时我们实际上不需要担心共享状态。我们可以只使用队列列表来实现这一点,并且知道两个任务不会意外地持有同一个馅饼。然而,强烈推荐使用 队列,因为它们允许我们通过提供暂停当前任务的合理点来非常轻松地在任务之间进行协作。

使用 的一个有趣方面是 关键字改变了函数的接口,因为它不能直接从非异步函数调用。这可以被认为是一件好事或坏事。一方面,你可以说它损害了可组合性,因为你不能混合 和普通函数。另一方面,如果 只用于 I/O,这会迫使 I/O 和业务逻辑分离,将 代码限制在应用程序的边缘,并使代码库更易于理解和测试。显式标记 I/O 是类型函数式语言中相当普遍的做法 - 在 中是必需的。

小结

Asyncio 的优点

对于 I/O 密集型任务处理非常快

由于只有一个系统线程,因此开销比线程少

所有最快的 Web 服务器框架都在使用 - 此处有一些benchmarks[2]

线程安全

Asyncio 的缺点

对于 CPU 密集型问题没有加速效果

需要 Python 3.5+

库支持适用于大多数 I/O 任务,但不如不使用 asyncio 完整

好了,这就是上篇的内容。如果大家觉得本文内容有帮助,请点赞转发支持一下。下篇将介绍并行的实践,并且总结该如何从 4 种并行和并发方案中做出选择。请持续关注哦~

参考资料

[1]

并发的艺术: https://www.oreilly.com/library/view/the-art-of/9780596802424/

[2]

benchmarks: https://www.techempower.com/benchmarks/#section=data-r19&hw=ph&test=fortune&l=zijzen-1r

[3]

参考原文: https://sourcery.ai/blog/concurrency/

- EOF -

标签: 公众号 python

最新资讯

文档百科

胤褆身为皇长子,为什么会被囚禁?

胤褆作为康熙帝长大成人的长子,自然是很受康熙帝宠爱,他本人也很循规蹈矩和勤奋努力,文能吟诗作对,武能骑马射箭。胤褆对自己犯下的过错非常悔恨,他多次命人向康熙帝请求赦免,但康熙帝坚决没有同意。

猫式瑜伽可以练哪个地方 猫式瑜伽怎么练

猫式瑜伽可以练哪个地方如果你家养小猫的话,相信你会发现,每当猫睡醒后都会蹬腿,然后伸个懒腰,猫式瑜伽就是模仿这个动作的,练习猫式瑜伽可以消除背部的僵硬和疲劳,且有一定的瘦腰功效,特别适合上班族。猫式瑜伽的体式要点1、跪在地上,两膝打开与臀部同一宽度,小腿及脚背紧贴在地上,脚板朝天。

突破健身平台期的方法

根据位于爱德华州迪比克城的Lora大学的头号力量教练JimRomagna所称,这一答案要视个人身体对训练状况的反应而定。Romagna解释说当你的身体适应以后就会停止进步。Romagna说我的训练哲学是:复合动作像深蹲和硬拉永远是任何训练方案的一部分,不管重点是力量训练还是肌肉增长。为了获得最大的力量和肌肉块,Romagna建议进行周期交替性训练。

东方的绿宝石!绿松石介绍以及雕刻鉴赏

绿松石用做玉器雕刻、中高档首饰、中国画颜料的矿物原料。绿松石制品颜色美丽,深受古今中外人们所喜爱。湖北郧阳地区被称为东方的绿宝石之乡,盛产的绿松石料质纯净,色泽艳丽,颜色多样,郧县云盖山绿松石矿为中国绿松石著名产地尤为珍贵。据说每得到100克,要挖1立方米岩石,由此可见绿松石的珍贵。

钻石怎样选购

2、用铅笔划:先把钻石用水湿润,然后再用铅笔轻轻的刻划,铅笔划过真钻石的晶面上是不留痕迹的,而如果不是钻石,就会在表面上留下痕迹。

昆明植物所喜马拉雅-横断山区高山草甸植物-熊蜂传粉网络研究取得新进展

传粉是植物与动物间互惠关系的重要组成部分。群落中不同植物与传粉者相互作用,构成复杂的传粉网络。相比于欧洲和北美,我国对熊蜂的基础研究仍然十分滞后,尤其是在生物多样性热点地区的喜马拉雅-横断山区,群落水平植物-熊蜂传粉网络的研究几乎为空白。

CopyRight © 2000~2023 一和一学习网 Inc.All Rights Reserved.
一和一学习网:让父母和孩子一起爱上学习