多线程、多进程,还是异步?-- Python 并发 API 如何选择

如何选择正确的 Python 并发 API模块 ?
Python 标准库提供了三种并发 API , 如何知道你的项目应该使用哪个 API?

在本教程将带逐步了解各API的特性、区别以及各自应用场景,指导你选择最合适的并发 API。

多线程、多进程,还是异步?-- Python 并发 API 如何选择

Python 标准库提供了三种在程序中并发执行任务的方法。分别是 :

  • multiprocessing 模块用于基于进程的并发,
  • threading 模块用于基于线程的并发,
  • asyncio 模块用于基于协程的并发。

实际上还有1个选择,在 Python标准库里,基于 threading 与 multiprocessing 提供了1个异步执行多任务的高阶API: concurrent.futures 模块,由于其使用 Pool Executor 执行器API, 因此,本文后面称之为Executors API.

很多人为选择哪个感到头痛:

  • 例如,在选择了一个模块之后,你应该使用工作池还是应该自己编写并发任务的代码?
  • 如果你选择使用工作池,你应该使用 multiprocessor Pools API 还是 Executors API?

即使是经验丰富的 Python 开发人员也对这些选项感到困惑。

你应该为你的项目使用哪个 Python 并发 API?

你需要一些经验法则来指导选择最合适的并发 API。

选择哪个 Python 并发 API?

你想在 Python 程序中使用并发。只有一个问题。你应该使用哪个 API? 这是我见到的最常见的问题之一。

这就是我写这本指南的原因。

首先,有三个主要的 Python 并发 API,它们是:

  • 基于协程,由 “asyncio” 模块提供。
  • 基于线程,由 “threading” 模块提供。
  • 基于进程,由 “multiprocessing” 模块提供。

在这三个之间选择相对直接。我将在下一节中向你展示如何做到这一点。

问题在于,还有更多的决定要做。你需要考虑是否应该使用可重用的工作池。

例如:

  • 如果你决定需要基于线程的并发,你应该使用线程池还是以某种方式使用 Thread 类?
  • 如果你决定需要基于进程的并发,你应该使用进程池还是以某种方式使用 Process 类?

然后,如果你决定使用可重用的工作池进行并发,你有多个选项可供选择。

例如:

  • 如果你决定需要线程池,你应该使用 ThreadPool 类还是 ThreadPoolExecutor
  • 如果你决定需要进程池,你应该使用 Pool 类还是 ProcessPoolExecutor

下图总结了这些决策点。

在这里插入图片描述

你如何弄清楚这一切?

你如何选择适合你项目的 Python 并发 API?

选择 Python 并发 API 的过程

你可以使用的一种方法,也许是最常见的方法,是临时选择一个 API。许多开发决策都是这样做出的,你的程序可能会很好地工作。但也许不会。

我建议在选择适合你项目的 Python 并发 API 时,采用 3 步过程判断:

步骤如下:

  1. 第 1 步:任务是基于CPU 绑定 vs IO 绑定(多进程 vs 线程)
  2. 第 2 步:需要并发执行许多临时任务 vs 只是一个复杂任务?
  3. 第 3 步:用池(Pool) vs 执行器(executor)?

我还把决策分析树绘成了一个方便的图片,如下:

在这里插入图片描述

接下来,让我们仔细看看每个步骤并增加一些细微差别。

第 1 步:CPU 绑定任务 vs IO 绑定任务?

选择要使用的 Python 并发 API 的第一步是考虑你想要执行的任务或任务的限制因素。

任务主要是 CPU 绑定还是 IO 绑定?

如果你正确理解这部分,你需要做的其他决定就变得不那么重要了。

让我们分别仔细看看每个方面。

CPU 绑定任务

CPU 绑定任务是一种涉及执行计算而不涉及 IO 的任务类型。这些操作只涉及内存中的数据,并对该数据执行计算。因此,这些操作的限制是 CPU 的速度。这就是为什么我们称它们为 CPU 绑定任务。

示例包括:

  • 估计圆周率。
  • 分解质数。
  • 解析 HTML、JSON 等文档。
  • 处理文本。
  • 运行模拟。

CPU 非常快,我们通常有一个以上的 多核CPU。我们希望执行我们的任务并充分利用现代硬件中的多个 CPU 核心。

现在我们已经熟悉了 CPU 绑定任务,让我们仔细看看 IO 绑定任务。

IO 绑定任务

IO 绑定任务是一种涉及从设备、文件或套接字连接中读取或写入的任务类型。这些操作涉及输入和输出 (IO),这些操作的速度受到设备、硬盘或网络连接的限制。这就是为什么这些任务被称为 IO 绑定的原因。

CPU 真的很快。现代 CPU,像 4GHz,每秒可以执行 40 亿条指令,你的系统中可能有一个以上的 CPU。与 CPU 的速度相比,做 IO 非常慢。与设备交互、读写文件和套接字连接涉及调用操作系统中的指令(内核),它将等待操作完成。如果这个操作是你的 CPU 的主要焦点,比如在你的 Python 程序的主线程中执行,那么你的 CPU 将等待很多毫秒,甚至很多秒,什么都不做。那是潜在的数十亿次操作,它被阻止执行。

IO 绑定任务的示例包括:

  • 从硬盘读取或写入文件。
  • 读取或写入标准输出、输入或错误(stdin、stdout、stderr)。
  • 打印文档。
  • 下载或上传文件。
  • 查询服务器。
  • 查询数据库。
  • 拍照或录像。
  • 等等。

现在我们已经熟悉了 CPU 绑定和 IO 绑定任务,让我们考虑我们应该使用哪种类型的 Python 并发 API。

选择 Python 并发 API

回想一下,multiprocessing 模块提供基于进程的并发,threading 模块提供进程内的基于线程的并发。

通常,如果你有 CPU 绑定任务,你应该使用基于进程的并发。

如果你有 IO 绑定任务,你应该使用基于线程的并发。

  • CPU 绑定任务:使用 “multiprocessing” 模块进行基于进程的并发。
  • IO 绑定任务:使用 “threading” 模块进行基于线程的并发。

multiprocessing 模块适用于主要关注计算或计算某物的任务,这些任务之间共享的数据相对较少。由于计算开销的增加,以及所有进程之间共享的数据都必须序列化,multiprocessing 不适用于任务之间发送或接收大量数据。

多进程可以让你可以利用每1个 CPU 核心或每个物理 CPU 核心来运行任务,并最大化利用底层硬件资源。

threading 模块适用于主要关注从 IO 设备读取或写入的任务,计算相对较少。由于全局解释器锁 (GIL) 阻止一次执行多个 Python 线程,threading 不适用于执行大量 CPU 计算的任务。GIL 通常只在执行阻塞操作时释放,比如 IO,或者在一些第三方 C 库中特别如此,比如 NumPy。

你可以执行数十、数百或数千个基于线程的任务,因为 IO 大部分时间都在等待。

下图总结了在线程模块的多线程并发和 multiprocessing 模块的进程并发之间进行选择的决策点。
在这里插入图片描述

你可以参考本人关于 threading 和 multiprocessing 的文章:

  • Python 多进程编程指南
  • Python 多线程编程指南

接下来,让我们考虑 AsyncIO 是否合适。

第 1.1 步 在线程和 AsyncIO 之间进行选择

如果你的任务主要是 IO 绑定的,你有另一个决策点。你必须在 “threading” 模块和 “asyncio” 模块之间进行选择。回想一下,threading 模块提供基于线程的并发,asyncio 模块提供线程内的基于协程的并发。

通常,如果你有很多套接字连接(或者更喜欢异步编程),你应该使用基于协程的并发。否则,你应该使用基于线程的并发。

  • 多个套接字连接:使用 “asyncio” 模块进行基于协程的并发。
  • 否则:使用 “threading” 模块进行基于线程的并发。

asyncio 模块专注于套接字连接的并发非阻塞 IO。例如,如果你的 IO 任务是基于文件的,那么 asyncio 可能不是合适的选择,至少仅因为这一点。

原因是协程比线程更轻量级,因此一个线程可以托管比进程可以管理的线程多得多的协程。例如,asyncio 可能允许成千上万,甚至更多的协程用于基于套接字的 IO,而 threading API 可能只有几百到低数千个线程。

另一个考虑是你可能会想要或需要在开发程序时使用异步编程范式,例如 async/wait。因此,这个要求将覆盖任务所施加的任何要求。同样,你可能对异步编程范式有反感,因此这种偏好将覆盖任务所施加的任何要求。

第 2 步:并发执行许多临时任务 vs 一个复杂任务?

第二步是考虑你是否需要执行独立的临时任务或一个大型复杂任务。

我们在这一点上考虑的是,你是否需要发出一个或多个可能从可重用工作池中受益的临时任务。或者,你是否需要一个单一任务,其中可重用工作池将是多余的。

另一种思考方式是,你是否有一个或几个不同但复杂的任务,如监视器、调度程序或类似的任务,可能会持续很长时间,例如程序的持续时间。这些将不是临时任务,并且可能不会从可重用工作池中受益。

  • 短暂和/或许多临时:使用线程或进程池。
  • 长期和/或复杂任务:使用 ThreadProcess 类。

在你选择基于线程的并发的情况下,选择是在线程池或使用 Thread 类之间。

在你选择基于进程的并发的情况下,选择是在进程池或使用 Process 类之间。

一些额外的考虑包括:

  • 异构 vs 同质任务:池可能更适合一组不同的任务(异构),而 Process/Thread 类适合一种类型任务(同质)。
  • 重用 vs 一次性使用:池适合重用并发的基础,例如重用线程或进程执行多个任务,而 Process/Thread 类适合一次性任务,可能是一个长期任务。
  • 多个任务 vs 单个任务:池自然支持多个任务,可能以多种方式发出,而 Process/Thread 类只支持一种类型的任务,一次配置或覆盖。

让我们通过一些例子来具体化:

  • 一个 for 循环,每次迭代调用一个函数,每次使用不同的参数,可能适合线程池,因为可以自动重用工作线程完成每个任务。
  • 一个监视资源的后台任务可能适合 Thread/Process 类,因为它是一个长期运行的单一任务,可能有很多复杂和专业的功能,可能分散在许多函数调用中。
  • 一个下载许多文件的脚本可能适合工作池,因为每个任务持续时间很短,可能有更多的文件而不是工人,允许重用工人和排队任务以完成。
  • 一个维护内部状态并与主程序交互的一次性任务可能适合 Thread/Process 类,因为类可以被覆盖以使用实例变量进行状态管理,使用方法进行模块化功能。

下图可能有助于在工作池和 ThreadProcess 类之间进行选择。
在这里插入图片描述

第 3 步:池 vs 执行器?

第三步是考虑要使用的工作者池的类型。

有两种主要类型,它们是:

  • multiprocessing.pool.Pool 和支持线程的类的移植 multiprocessing.pool.ThreadPool
  • 执行器concurrent.futures.Executor 类和两个子类 ThreadPoolExecutorProcessPoolExecutor

两者都提供工作者池。相似之处众多,差异很少且微妙。

例如,相似之处包括:

  • 两者都有线程和基于进程的版本。
  • 两者都可以执行临时任务。
  • 两者都支持同步和异步任务执行。
  • 两者都提供检查状态和等待异步任务的支持。
  • 两者都支持异步任务的回调函数。

选择一个而不是另一个对你的程序不会有太大影响。主要区别在于每个提供的 API,特别是在任务处理的重点或方式上的微小差异。

例如:

  • 执行器提供取消已发布任务的能力,而池则没有。
  • 执行器提供处理不同类型任务集合的能力,而池则没有。
  • 执行器没有强制终止所有任务的能力,而池有。
  • 执行器没有提供多个并行版本的 map() 函数,而池有。
  • 执行器提供访问任务中引发的异常的能力,而池则没有。

我认为,关于池(Pool)真正重要是,池专注于使用许多不同版本的 map() 函数进行并发 for 循环,可将函数应用于一个可迭代参数中的每个参数。

执行器具有这种能力,但重点更多地是发布临时任务并异步管理任务集合。

下图有助于总结池和执行器之间的差异。
在这里插入图片描述

结束语

现在你知道如何在不同的 Python 并发 API 之间进行选择。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/405876.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

F1 F4 Fn lock 指示灯不亮 联想笔记本 thinkpad

问题描述:F1 F4 Fn lock 指示灯开机的时候亮,但是使用的时候虽然能够发挥正常功能,但是指示灯一直熄灭,指示灯不亮。 电脑型号:联想笔记本 thinkpad E14 Gen 2 。本方案应该适用于所有联想电脑。 解决方法:…

鸿蒙内核源码分析(静态链接篇) | 完整小项目看透静态链接过程

下图是一个可执行文件编译,链接的过程. 本篇将通过一个完整的小工程来阐述ELF编译,链接过程,并分析.o和bin文件中各区,符号表之间的关系.从一个崭新的视角去看中间过程. 准备工作 先得有个小工程,麻雀虽小&#xff0…

基于数据复杂度的数据库选型

数据模型的选择对于 IT 系统的开发至关重要,它不仅决定了数据存储和处理的方式,影响系统的性能、扩展性以及维护性等。本质上来说,不同的数据模型反映了我们对业务问题的不同思考和抽象程度。 今天我们从不同数据模型对于复杂数据和关系的支…

【Qt】常用控件QCalendarWidget的使用

常用控件QCalendarWidget的使用 QCalendarWidget表示一个日历 核心属性 属性说明 selectDate 当前选中的⽇期 minimumDate 最⼩⽇期 maximumDate 最⼤⽇期 firstDayOfWeek 每周的第⼀天(也就是⽇历的第⼀列) 是周⼏. gridVisible 是否显⽰表格的边框 selectionMode…

python3爬虫(未完结)

一个简单的例子:爬取自己的csdn博客,统计每篇博客的访问量,制作一个柱状图,以访问量从大到小的方式显示。 1. 首先从“个人主页”爬取所有所有文章的链接 1.1 打开个人主页,右键->检查:可以看到每篇文章…

类和对象(下)(2)

类和对象(下)(2) static成员 • ⽤static修饰的成员变量,称之为静态成员变量,静态成员变量⼀定要在类外进⾏初始化。 • 静态成员变量为当前类的所有对象所共享,不属于某个具体的对象,不存在对象中&#…

HiveSQL实战——大厂面试真题

一、字节跳动 最高峰同时直播人数 https://blog.csdn.net/SHWAITME/article/details/135918264 0 问题描述 有如下数据记录直播平台主播上播及下播时间,根据该数据计算出平台最高峰同时直播人数。 ------------------------------------------------------ | us…

CTFHUB | web进阶 | JSON Web Token | 无签名

一些JWT库也支持none算法,即不使用签名算法。当alg字段为空时,后端将不执行签名验证 开启题目 账号密码随便输,登录之后显示只有 admin 可以获得 flag 在此页面抓包发到 repeater,这里我们需要用到一个 Burp 插件,按图…

Linux信号机制探析--信号的产生

🍑个人主页:Jupiter. 🚀 所属专栏:Linux从入门到进阶 欢迎大家点赞收藏评论😊 目录 📚信号什么是信号?为什么要有信号?查看Linux系统中信号 🎈信号产生📕kill…

【流媒体】RTMPDump—RTMP_ConnectStream(创建流连接)

目录 1. RTMP_ConnectStream函数1.1 读取packet(RTMP_ReadPacket)1.2 解析packet(RTMP_ClientPacket)1.2.1 设置Chunk Size(HandleChangeChunkSize)1.2.2 用户控制信息(HandleCtrl)1…

JAVA面试汇总

JAVA面试 JAVA面试精华 面试精华 互联网面试真题

keepalived详解

概念 keepalived 是一款基于 VRRP(Virtual Router Redundancy Protocol,虚拟路由冗余协议)协议来实现高可用(High Availability, HA)的轻量级软件。它主要用于防止单点故障,特别是在 Linux 环境下&#xff…

使用maven快速生成打包文件

最近在部署基于SpringBoot开发的项目时,由于微服务较多,本地工程编译后只得出一个JAR包,部署起来实在不方便,因此总想着怎么偷偷懒,执行一次命令编译出整个部署的文件。先说结果,最后期望打包的目录如下&am…

C++ | 继承

前言 本篇博客讲解c中的继承 💓 个人主页:普通young man-CSDN博客 ⏩ 文章专栏:C_普通young man的博客-CSDN博客 ⏩ 本人giee: 普通小青年 (pu-tong-young-man) - Gitee.com 若有问题 评论区见📝 🎉欢迎大家点赞&…

Kubernetes 如何给pod的 /etc/hosts文件里面添加条目

创建pod的时候,pod会在其/etc/hosts里面添加一个条目。 [rootmaster ~]# kubectl get pod -o wide NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES dns-test 1/1 R…

LLM概念梳理(二):检索增强RAG

非常感谢RAG(检索增强生成)技术详解:基于垂直领域专有数据的 Chatbots 是如何实现的,这篇文章对 RAG 技术进行了详细的描述。我根据自己的理解,并且按照代码思路重新进行整理。 RAG 技术看似神奇,其本质是…

图片怎么压缩得小一点?这八种免费图片压缩方法赶紧试试

在数字化时代,无论是工作还是日常生活中,图片的使用已变得不可或缺。然而,随着高分辨率图片的广泛应用,文件体积也随之增加,这不仅占用了大量存储空间,还可能导致传输和加载速度变慢。因此,如何…

干货:2024必备的四大PDF编辑器推荐!

面对PDF文件的编辑需求,你是否感到无从下手?那么,今天就为大家推荐几款实用的PDF编辑工具,让你轻松应对各种PDF编辑难题。 福昕PDF编辑器 链接:editor.foxitsoftware.cn 福昕PDF编辑器多功能专业级是我PDF编辑器。它…

python-docx 实现 Word 办公自动化

前言:当我们需要批量生成一些合同文件或者简历等。如果手工处理对于我们来说不仅工作量巨大,而且难免会出现一些问题。这个时候运用python处理word实现自动生成文件可极大的提高工作效率。 python-docx是python的第三方插件,用来处理word文件…

HTML 列表和容器元素——WEB开发系列10

HTML 提供了多种方式来组织和展示内容&#xff0c;其中包括无序列表、有序列表、分区元素 ​​<div>​​ 和内联元素 ​​<span>​​、以及如何使用 ​​<div>​​​ 进行布局和表格布局。 一、HTML 列表 1. 无序列表 (​​<ul>​​) 无序列表用于展…