是的,我已经停止在这里发布,但从营销的角度来看,最好继续发布......我们继续。
文本最初发布于此处。
本文的目的是直接总结理解 Python 语言中的并发和并行性所需的基本概念。我建议您了解该主题的最低背景知识,或者将本文与其他来源的研究结合起来。所有参考资料均在文末。
我将涵盖以下主题:
在计算中,进程是正在运行的应用程序的实例。如果您在计算机上打开一个应用程序(例如浏览器),该应用程序将与某个进程关联。一个进程由以下部分组成:
以下图片取自 Francis Machado 和 Luis Maia 的书:
此信息是执行程序所必需的。
线程是程序的子例程,是操作系统管理的最小执行单元,也是进程的组件。
假设进程的各个线程可以并发执行(我们很快就会理解),共享内存等资源。不同的进程不共享这些资源。
下图取自维基百科:
解释上图,我们可以得出,一个程序保存在磁盘(辅助非易失性存储器)上,包含多个指令,并且可以在一个或多个进程中实例化(启动),而这些又可以具有几个相关的线程。
这两个表达在有关竞争的讨论中出现很多,并且可以在葡萄牙语中与 I/O(输入/输出)和 CPU(中央处理单元)一起出现。
当我们谈论 I/O 限制和 CPU 限制时,我们谈论的是阻止操作在计算机上更快运行的限制因素,我们可以在同一代码库中找到这两种类型的操作。
CPU 密集型操作是 CPU 密集型操作,如果 CPU 更强大,运行速度会更快。换句话说,如果我们将时钟速度从 2GHz 提高到 4GHz,该操作可能会运行得更快。我们在这里讨论的是执行许多计算、计算的操作;例如,如何计算 Pi。
I/O 绑定操作取决于网络速度以及输入和输出设备的速度。向 Web 服务器发出请求或从磁盘读取文件是 I/O 绑定操作。
两种类型的操作都可以从并发的使用中受益。
GIL 代表全局解释器锁,旨在防止一个Python 进程同时执行多个Python 指令字节码。要运行一个线程,必须“获取”GIL,而当一个线程持有 GIL 时,另一个线程无法同时获取它。这并不意味着我们在这种情况下不能有多个线程。
这里我们正在考虑Python参考实现。 CPython 是 Python 的标准实现,用作该语言行为方式的参考。还有其他实现,例如 Jython 或 IronPython。 GIL 存在于 CPython 中,直到最近我们才收到 PEP(Python 增强提案)建议将 GIL 设为可选。
GIL 的想法是防止竞争条件,当多个线程需要同时引用一个 Python 对象时,就会出现这种情况。如果多个线程修改共享变量,则该变量可能会处于意外状态。图片取自 Matthew Fowler 的书:
在上图中,两个线程试图同时增加引用计数,由于两个线程都增加了 1,所以最终结果给出了 1(每个线程是一列),而不是计数给出 2。
当处理多个任务时,就会发生计算竞争,而不必完全同时执行这两个任务。 Rob Pyke 关于这个主题的著名短语:
竞争意味着同时处理很多事情。并行性是同时做很多事情。
想象一下这个假设的情况:如果你要做两个蛋糕,你可以先预热烤箱,同时准备第一个蛋糕的面团。一旦烤箱达到正确的温度,您就可以将第一个蛋糕的面团放入烤箱,在等待蛋糕在烤箱中发酵的同时,您可以准备第二个蛋糕的面团。竞争的思路基本上就是这样,你不需要闲着、卡住、停止,在等待任务完成的时候,你可以做一个切换,改变任务。
在这种情况下,我们有两种类型的多任务处理:
下图有助于总结 Python 中的并发性:
并行意味着多个任务同时执行。换句话说,并行意味着并发(处理多个任务),但并发并不意味着并行(任务不一定同时并行执行)。为了使并行性成为可能,我们需要多个 CPU 核心。
在 Python 中,并行性是通过多处理库实现的,其中我们将拥有多个 Python 进程,每个进程都有自己的 GIL。该图有助于说明 Python 中的并行性:
在 Python 中实现并发和并行有不同的方法,我们可以使用一些库来优化我们的代码,具体取决于我们正在处理的操作类型,I/O 限制或 CPU 限制。 asyncio 是一个使用 async 和await 实现并发的库。来自文档:
Asyncio 被用作多个异步 Python 框架的基础,这些框架提供高性能网络和 Web 服务器、数据库连接库、分布式作业队列等。
正如你想象的那样,这个库适合优化 I/O 密集型任务,其中有网络等待时间、写入磁盘等。在 CPU 密集型操作中,没有等待,我们只依赖于 CPU 计算速度。
Python 的线程库允许我们操作多个线程,但是,我们仍然处理一个 CPU 核心和一个 Python 进程,请记住,这是操作系统执行抢占式多任务处理的一种情况我们的任务切换。该库对于优化 I/O 绑定操作也更有用。
关于线程,Real Python 网站提供了一些要点:
由于操作系统控制一个任务何时停止以及另一个任务何时启动,因此线程之间共享的任何数据都需要受到保护,或者线程安全。不幸的是 requests.Session() 不是线程安全。有多种策略可以使数据访问线程安全,具体取决于数据是什么以及您如何使用它。其中之一是使用线程安全数据结构作为Python队列模块的Queue。
我们在这里找到了队列文档。
关于Python文档中的多处理库:
multiprocessing 是一个支持使用类似于 threading 模块的 API 生成进程的包。多处理包提供本地和远程并发,通过使用子进程而不是线程有效地绕过 GIL。这就是为什么多处理模块允许程序员利用一台机器上的多个处理器。
值得指出的是,在不同的CPU核心上运行多个进程并不意味着禁用GIL,而是每个进程都会有自己的GIL。通过利用多个 CPU 核心,在多个可用核心之间分担繁重的 CPU 工作负载,该库更适合 CPU 限制。
来源:
福勒,马修。 Python 与 asyncio 的并发。曼宁出版社,2022 年。
马查多,弗朗西斯·贝伦热;玛雅,路易斯·保罗。操作系统架构:包括 SOSIM 模拟器练习和 ENADE 问题。里约热内卢:LTC,2013。
维基百科的线程(计算)
通过真正的 Python 并发来加速你的 Python 程序
以上是Python 中的并发和并行的详细内容。更多信息请关注PHP中文网其他相关文章!