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

news2024/11/26 4:38:56

如何选择正确的 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.coloradmin.cn/o/2061148.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

音频采集spring_ws_webrtc (html采集麦克风转gb711并发送广播播放)完整案例

下载地址:http://www.gxcode.top/code 项目说明 springbootwebscoektwebrtc 项目通过前端webrtc采集麦克风声音,通过websocket发送后台,然后处理成g711-alaw字节数据发生给广播UDP并播放。 后台处理项目使用线程池(5个线程)接受webrtc数据并…

JAVA基础:字节字符转换流

前言 当我们使用流读取数据时,如果这个数据中含有中文,我们对这个数据进行操作就会出现乱码问题,这时候我们要使用字节字符转换流来处理一下数据。 字节字符转换流 字节字符转换流是一个过程流 字节字符转换流是一个字符流,所…

如何为 SEO 做关键词优化

关键词优化是一种基本的 SEO 技术,可以提高你的网站在搜索结果中的可见度,并吸引更多的访问者。 在这份关键词优化指南中,我们将分享在 SEO 的这一方面脱颖而出所需的知识、技巧和技巧。使用本指南可以超越您的竞争对手,并为您的…

一文搞懂数据标注

hihi,大家好,最近开始和内容那个团队研究SD出图了,作为家具家电行业的小牛人,除了研究SD和ComfyUI本身,需要了解算法同学如何进行模型微调的,因为用的是开源的底座,预训练部分就不深入介绍了,重点看一下二次预训练,在这个过程中有一个环节比较关注就是小规模数据集,也…

【java】RuoYi-Vue前后端分离版本-请求被拦截,怎么修改拦截过滤器,解决方案

【java】RuoYi-Vue前后端分离版本-请求被拦截,怎么修改拦截过滤器 它用到了一个安全管理框架Spring Security 你可以通过这篇文章《Spring Security 详解》 去了解它,怎么使用或者使用原理。 所有业务都受SecurityConfig配置所过滤 SecurityConfig配置…

2024年思维导图工具怎样可以轻松选择

思维导图,作为一种直观、有效的思维工具,凭借其强大的信息整理能力和创意激发潜力,逐渐成为了人们学习、工作和生活中不可或缺的一部分。今天,就让我们一起走进这个充满智慧的领域,探索那些2024年大家都在使用的思维导…

Cleer耳机好不好用?南卡、Cleer、飞利浦、倍思横评对比

​大家好,作为一位多年的数码博主和耳机发烧友,最近后台收到不少私信,都是小伙伴想让我测评一下最近很火热的开放式耳机,那么为了回馈大家这么久的支持,说做就做!我买了最近网上很热门的几款开放式耳机&…

电线 硬线 和软线 连接

电线 硬线 和软线 连接 首先把软线,拧成一股,然后搭到硬线上回转一圈,这里要注意线头往反向回转,大家看下图中的软线, 再在硬线上缠绕7—8圈,根据线的长短,过长就剪去一些。 最后将硬导线往后弯…

【速览】设计模式(更新中)

目录 一、背景二、优缺点优点缺点 三、适用场景四、核心组成创建型模式 Creational Design Pattern 5结构型模式 Structural Design Patterns 7行为型模式 Behavioral Design Patterns 11’ 五、底层原理六、对比参考 一、背景 这个技术出现的背景、初衷和要达到什么样的目标或…

【Python开发实践】在线商城系统——需求及需求分析

项目背景及需求 这个练习项目的设置背景是一家图书销售公司,为了扩大销售渠道,想要开通网上商城,利用在线博客和电子商城来销售图书。 具体需求如下: 每个商品可以留言 实现在线购物车处理和订单处理 实现对产品、购物车和订单的…

python | 图片转换为 pdf 实现方法

目录 一、PIL 库简介及安装使用方法 (一)python 不同版本下 PIL 的使用方法 二、图片转换为 pdf 的两种实现方法 (一)简易版——pdf 页面尺寸跟随图片大小 (二)常用版——pdf 每页尺寸统一为 A4 一、P…

C#PACS系统源码,影像存档与传输系统源码,数字化医学影像系统源码,三维重建影像PACS系统源码

C#PACS系统源码,影像存档与传输系统源码,数字化医学影像系统源码,三维重建影像PACS系统源码 PACS即影像存档与传输系统(Picture Archiving and Communication System),是医学影像、数字化图像技术、计算机技…

C++遍历for_each

#include <iostream> #include <stack> #include <vector> #include <algorithm> using namespace std; //打印数组 class bianli { public:void operator()(int val){cout << val << " ";} }; void printVector(int val) {cout…

【经验分享】将ui文件转化成py代码

目录 先写一个简单的ui界面 将ui文件另存为 将UI文件转换为py代码 测试一下你生成的py文件 &#x1f31f; 嗨&#xff0c;我是命运之光&#xff01; &#x1f30d; 2024&#xff0c;每日百字&#xff0c;记录时光&#xff0c;感谢有你一路同行。 &#x1f680; 携手启航&…

第R2周:LSTM-火灾温度预测

&#x1f368; 本文为&#x1f517;365天深度学习训练营 中的学习记录博客&#x1f356; 原作者&#xff1a;K同学啊 一、什么是LSTM 1.LSTM的本质 长短时记忆网络&#xff08;Long Short-Term Memory, LSTM&#xff09;的本质是一种特殊的循环神经网络&#xff08;Recurrent…

《Cloud Native Data Center Networking》(云原生数据中心网络设计)读书笔记 -- 05网络虚拟化

本章帮助网络工程师或架构师回答如下问题&#xff1a; 什么是网络虚拟化?网络虚拟化有哪些用途?网络虚拟化领域内有哪些不同的技术方向?网络虚拟化的控制面有哪些选择?当使用 VXLAN 时如何进行桥接和路由&#xff1f; 什么是网络虚拟化&#xff1f; 网络虚拟化可以让网络…

揭秘!格行如何成为随身WiFi界的‘比亚迪’!如何成为随身WiFi热销第一名?

比亚迪把续航超过2000km的油车打入了10万元以内&#xff01;创造历史&#xff01; 为什么这么说&#xff1f;还不是因为这两辆车都搭载了比亚迪最新研发的第五代 Dmi 混动技术。这项技术有多“逆天”&#xff1f;直接创下了三项全球之最——全球最高发动机热效率、百公里最低油…

CUDA C++ 最佳实践指南

CUDA C 最佳实践指南 (nvidia.com)https://docs.nvidia.com/cuda/cuda-c-best-practices-guide/index.html#getting-started2. 异构计算 CUDA 编程涉及在两个不同的平台上同时运行代码&#xff1a;具有一个或多个 CPU 的主机系统和一个或多个支持 CUDA 的 NVIDIA GPU 设备。 …

Hermite 三次插值可编辑样条

欢迎关注更多精彩 关注我&#xff0c;学习常用算法与数据结构&#xff0c;一题多解&#xff0c;降维打击。 问题描述 利用三次样条和Hermite插值制作一个类似于ppt里的曲线工作。 基本思路 对于给定点&#xff0c;初始生成时用参数化三次函数插值的方式生成。三次样条函数插…

【Py/Java/C++三种语言详解】LeetCode743、网络延迟时间【单源最短路问题Djikstra算法】

可上 欧弟OJ系统 练习华子OD、大厂真题 绿色聊天软件戳 od1441了解算法冲刺训练&#xff08;备注【CSDN】否则不通过&#xff09; 文章目录 相关推荐阅读一、题目描述二、题目解析三、参考代码PythonJavaC 时空复杂度 华为OD算法/大厂面试高频题算法练习冲刺训练 相关推荐阅读 …