当前位置: 首页 > news >正文

python异步编程 -什么是python的异步编程, 与多线程和多进程的区别

什么是异步执行

核心思想: 异步编程是一种编程模式,它允许一个操作“在后台”启动,而无需等待该操作完成,当前线程可以立即去执行其他任务。当那个后台操作完成后,再通过回调、承诺或事件等方式通知程序,并处理其结果。





厨师的例子

假如一个厨师需要做 3道菜, 分别是褒汤(1小时), 用烤箱土豆(40分钟),炒饭(10分钟)





同步方式

厨师严格按照顺序做菜,做完一道再做下一道。

  1. 开始煲汤,花费 1 小时。
  2. 汤煲好后,开始烤土豆,花费 40 分钟。
  3. 土豆烤好后,开始炒饭,花费 10 分钟。
    总耗时:1 小时 + 40 分钟 + 10 分钟 = 1 小时 50 分钟。

注: 同步方式下, 厨师每开始一个任务后都是阻塞状态, 也就是讲必须做完一件再开始另一个任务, 这也是python 程序的默认处理方式。





异步方式

厨师可以同时处理多个任务,把需要长时间等待的任务先启动起来,然后利用等待时间去做其他事情。

  1. 先把汤煲上,这个任务需要 1 小时,但不需要一直盯着。
  2. 在煲汤的同时,把土豆放进烤箱,这个任务需要 40 分钟。
  3. 在等待汤和土豆的同时,开始炒饭,10 分钟后炒饭完成。
  4. 厨师可以休息或准备其他事情,直到土豆和汤完成。
    总耗时:取决于耗时最长的任务,即 1 小时。

注: 异步方式下, 任务分为非阻塞的(褒汤, 烤土豆 )和阻塞的(炒饭), 其实异步方式对多个非阻塞的任务很有效, 但是对阻塞性的任务帮助不大。





多线程方式

可以想象成一个厨房里(一个进程),有一个总指挥厨师(主线程)和几个助手厨师(子线程)。他们共享厨房里的工具(内存、资源)。

  1. 总指挥厨师让助手A去煲汤。
  2. 总指挥厨师让助手B去烤土豆。
  3. 总指挥厨师自己去炒饭。
    大家在同一个厨房里同时开工,提高了效率,但需要小心协调,避免互相干扰(比如资源竞争)。
    总耗时:也取决于最长的任务,即 1 小时,但会有一些协调(线程切换)的开销。





多进程方式

可以想象成雇佣了三个厨师,每人一个独立的厨房(独立的进程),每个厨房的资源(锅、灶台)都是独立的。

  1. 厨师A在厨房A里煲汤。
  2. 厨师B在厨房B里烤土豆。
  3. 厨师C在厨房C里炒饭。
    他们之间完全独立,互不干扰。这种方式资源隔离性最好,但开销也最大(相当于建了三个厨房)。
    总耗时:同样取决于最长的任务,即 1 小时,但前期准备(创建进程)的开销较大。





阻塞性任务和非阻塞性任务

在编程中,任务可以根据其执行方式分为两类:阻塞性任务和非阻塞性任务。




阻塞性任务 (Blocking Tasks)

定义: 阻塞性任务是指在任务完成之前,会“阻塞”当前程序的执行流程,程序会一直等待,直到该任务返回结果。在等待期间,程序无法执行任何其他操作。

特点:

  • 同步执行: 任务按顺序依次执行。
  • 资源占用: 在等待 I/O 操作(如读写文件、网络请求)或长时间计算时,会占用 CPU 时间片,即使它大部分时间在等待。

例子:

  • CPU 密集型任务: 大量复杂的数学计算、图像处理、数据加密等。
  • 同步的 I/O 操作: time.sleep()、不使用异步库的网络请求、读取大文件等。

回到厨师的例子,炒饭可以看作一个阻塞性任务。厨师必须站在锅前不停地翻炒,直到炒饭完成,这个过程他无法离开去做别的事情。




非阻塞性任务 (Non-blocking Tasks)

定义: 非阻塞性任务是指启动后会立即返回,不会阻塞当前程序的执行。程序可以继续执行其他任务,而该任务在后台运行。当它完成后,会通过某种机制(如回调函数、Promise、事件循环)通知程序。

特点:

  • 异步执行: 允许并发处理多个任务。
  • 高效利用资源: 特别适合 I/O 密集型任务。在等待数据返回时,CPU 可以切换去处理其他任务,而不是空等。

例子:

  • I/O 密集型任务: 网络请求、数据库查询、文件读写等。当这些任务使用异步库(如 Python 的 asyncio、JavaScript 的 async/await)执行时,它们就是非阻塞的。

在厨师的例子中,煲汤烤土豆就是典型的非阻塞性任务。厨师只需要把食材放进锅里或烤箱,设置好时间,然后就可以离开去做其他事情(比如炒饭),只需要偶尔回来检查一下状态即可。






所以

特性异步编程 (Async)多线程 (Multi-threading)
并发模型协作式多任务,单线程内通过事件循环切换抢占式多任务,操作系统强制切换线程
资源开销非常低,仅需一个线程较高,每个线程都有独立的堆栈和上下文
CPU 利用无法利用多核处理单个任务可以利用多核并行执行任务
适用场景I/O 密集型,高并发,大量等待CPU 密集型,需要并行计算
数据共享默认安全(单线程),无需加锁复杂,需要使用锁等同步机制来避免竞争
编程难度相对较高,需要理解事件循环和回调极高,调试困难,容易出现死锁等问题
切换开销非常小,只是函数调用较大,涉及内核态和用户态的切换

总结:

  • 当你的任务主要是等待(如等待网络响应)时,选择异步编程
  • 当你的任务主要是计算(需要多核并行处理)时,选择多线程多进程




什么是python的GIL锁

GIL是CPython解释器中的一个互斥锁

它防止多个线程同时执行Python字节码

这意味着在任何时刻,只有一个线程可以执行Python代码

GIL的影响:

CPU密集型任务:多线程无法利用多核CPU实现并行计算

I/O密集型任务:在等待I/O时,线程会释放GIL,其他线程可以运行

所以GIL的存在让python无法真正地多线程执行任务
虽然python也提供多线程的类, 但是实际执行效果并不佳





python的多进程编程

由于 GIL 的存在,Python 的多线程在 CPU 密集型任务上无法发挥多核优势。为了真正实现并行计算,Python 提供了 multiprocessing 模块,它允许我们创建多个进程来执行任务。




为什么使用多进程?

  • 绕过 GIL: 每个进程都有自己独立的 Python 解释器和内存空间,因此每个进程都有自己的 GIL。这使得多进程可以真正地在多个 CPU 核心上并行执行代码。
  • 利用多核 CPU: 对于 CPU 密集型任务(如科学计算、数据分析、图像处理),多进程是提升性能的关键。
  • 稳定性: 进程之间相互独立,一个进程的崩溃不会影响其他进程,主进程也可以更好地管理子进程。




multiprocessing 模块的基本使用

Python 的 multiprocessing 模块提供了与 threading 模块相似的 API,使其易于上手。




1. 使用 Process

你可以创建一个 Process 对象,并告诉它要执行哪个函数。

import multiprocessing
import time
import osdef worker(num):"""子进程要执行的工作"""print(f"Worker {num} started, PID: {os.getpid()}")time.sleep(2)print(f"Worker {num} finished")if __name__ == "__main__":processes = []for i in range(5):# 创建一个进程p = multiprocessing.Process(target=worker, args=(i,))processes.append(p)# 启动进程p.start()for p in processes:# 等待所有子进程完成p.join()print("All processes finished.")

注意: 在 Windows 和 macOS 上,创建子进程的代码必须放在 if __name__ == "__main__": 块中,以避免无限递归地创建子进程。




2. 使用 Pool (进程池)

当需要处理大量任务时,手动创建和管理进程会很繁琐。Pool 类可以帮助我们方便地管理一个进程池。

import multiprocessing
import timedef square(x):"""计算一个数的平方"""time.sleep(1)return x * xif __name__ == "__main__":# 创建一个包含4个进程的进程池with multiprocessing.Pool(processes=4) as pool:numbers = [1, 2, 3, 4, 5, 6, 7, 8]# 使用 map 方法将任务分配给进程池# 它会阻塞直到所有结果都准备好results = pool.map(square, numbers)print("Results:", results)print("All tasks finished.")





进程间通信 (IPC)

由于进程拥有独立的内存空间,它们之间不能像线程一样直接共享数据。multiprocessing 模块提供了多种进程间通信(Inter-Process Communication, IPC)的机制:

  • Queue 一个线程和进程安全的队列,允许在多个进程之间传递消息。
  • Pipe 创建一个管道,返回一对连接对象,代表管道的两端。
  • Manager 支持更多的数据类型,如 list, dict, Namespace 等,允许在不同进程间共享状态。

多进程的缺点

  • 资源开销大: 创建和维护进程的开销远大于线程。每个进程都需要独立的内存空间,会消耗更多内存。
  • 通信复杂: 进程间通信比线程间共享数据要慢,且需要显式处理。

总结

  • 多进程是解决 Python 中 CPU 密集型任务并行计算的最佳选择
  • 它通过创建独立的进程来绕过 GIL 的限制,充分利用多核 CPU。
  • 使用 multiprocessing 模块可以方便地创建和管理进程,但需要注意其资源开销通信成本

全文总结:如何选择正确的并发模型?

在 Python 中选择正确的并发模型是优化程序性能的关键。通过本文的介绍,我们可以得出一个清晰的选择指南:

  1. 异步编程 (AsyncIO):

    • 核心优势: 在单线程内实现高并发,资源开销极低。
    • 最佳场景: I/O 密集型任务,如大量的网络请求、数据库连接、文件读写等。当程序大部分时间在“等待”时,异步是最高效的选择。
    • 不适用: CPU 密集型任务。
  2. 多线程 (Threading):

    • 核心优势: 应对阻塞性 I/O,防止程序假死。API 相对简单。
    • 最佳场景: I/O 密集型任务,特别是当需要与一些不支持异步的旧版库交互时。
    • 局限: 受 GIL 限制,无法利用多核 CPU 进行并行计算,不适用于 CPU 密集型任务。
  3. 多进程 (Multiprocessing):

    • 核心优势: 绕过 GIL,能够充分利用多核 CPU 实现真正的并行计算。
    • 最佳场景: CPU 密集型任务,如复杂的科学计算、数据处理、视频编码等。
    • 缺点: 资源开销最大,进程间通信比线程复杂。

最终决策流程

  • 你的任务是计算密集型吗?
    • : 毫不犹豫地选择 multiprocessing
  • 你的任务是 I/O 密集型吗?
    • :
      • 优先选择 异步编程,因为它性能更高,资源占用更少。
      • 如果代码中包含无法改为异步的阻塞库,或者项目逻辑相对简单,可以考虑使用多线程
http://www.dtcms.com/a/473399.html

相关文章:

  • Linux系统--进程间通信--共享内存相关指令
  • 网站开发的实践报告石家庄市工程勘察设计咨询业协会
  • TensorFlow深度学习实战——图分类
  • SAP MM采购信息记录维护接口分享
  • 网站搭建装修风格大全2021新款简约
  • Mysql初阶第八讲:Mysql表的内外连接
  • SpringCloud 入门 - Gateway 网关与 OpenFeign 服务调用
  • uniapp 选择城市(城市列表选择)
  • AR小白入门指南:从零开始开发增强现实应用
  • 02_k8s资源清单
  • 2025年渗透测试面试题总结-109(题目+回答)
  • uniapp配置自动导入uni生命周期等方法
  • flink的Standalone-HA模式安装
  • Flink时态表关联:实现数据“时间旅行”的终极方案
  • 做哪类英文网站赚钱wordpress 页面 列表
  • nginx + spring cloud + redis + mysql + ELFK 部署
  • 【黑马点评 - 实战篇01】Redis项目实战(Windows安装Redis6.2.6 + 发送验证码 + 短信验证码登录注册 + 拦截器链 - 登录校验)
  • 汕头市通信建设管理局网站二网站手
  • FreeRTOS小记
  • 数据结构实战:顺序表全解析 - 从零实现到性能分析
  • 【C++进阶】继承上 概念及其定义 赋值兼容转换 子类默认成员函数的详解分析
  • 华为matebook16s 2022禁用触摸板和触摸屏操作
  • GridRow 和 Column 有啥区别
  • 030159网站建设与维护中国科技成就素材
  • Echarts 5.6.0 Grid 坐标系中 Y 轴可视化的优化之路
  • Java 线程池如何知道一个线程的任务已经执行完成
  • JVM字节码与类的加载(一):类的加载过程详解
  • 强军网网站建设网站需要备案才能建设吗
  • 耄大厨——AI厨师智能体(3-工具调用)
  • (二)黑马React(导航/账单项目)