news 2026/6/24 10:58:06

Python语法进阶笔记(七)

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Python语法进阶笔记(七)

一、协程介绍

  1. 概念:

    • 单线程下的开发,又称微线程,纤程。英文名叫 Coroutine。

    • 协程是 python 中另外一种实现多任务的方式,只不过比线程更小、占用更小执行单元(理解为需要的资源)。它自带 CPU 上下文。这样只要在合适的时机,我们可以把一个协程切换到另一个协程。只要这个过程中保存或恢复 CPU 上下文那么程序还是可以运行的。

    • 线程和进程的操作是由程序触发系统接口,最后的执行者是系统,协程的操作则是程序员。

  2. 简单实现协程

    def task1(): while True: yield "正在执行任务1" def task2(): while True: yield "正在执行任务2" if __name__ == "__main__": while True: print(next(task1())) print(next(task2())) # 输出一个死循环
  3. 应用场景

    1. 如果一个线程里面有IO 操作比较多的时候,可以用协程(IO:Input/Output)

      常见的IO操作:文件操作、网络请求

    2. 适合高并发处理

二、greenlet 的使用

  1. 介绍:greenlet是一个由C语言实现的协程模块。通过设置switch()来实现任意函数之间的切换

  2. 安装与卸载命令:win +r 后输入cmd

    • 安装greenlet

      (python -m) pip install greenlet

    • 卸载greenlet

      (python -m) pip uninstall greenlet

    • 查看安装包

      (python -m) pip list

  3. 注意:greenlet属于手动切换,当遇到IO操作,程序会阻塞,而不能进行自动切换

  4. 通过greenlet实现任务的切换

    1. 导入模块 from greenlet import greenlet

    2. 创建协程对象,调用switch()

    from greenlet import greenlet def sing(): print("正在唱歌...") g2.switch() #切换到g2中去运行 print("唱完歌了") def dance(): print("正在跳舞...") print("跳完舞了") g1.switch()#切换到g1中去运行 if __name__ == "__main__": # 创建协程对象 g1 = greenlet(sing) g2 = greenlet(dance) g1.switch() #切换到g1中去运行 # 正在唱歌... # 正在跳舞... # 跳完舞了 # 唱完歌了

三、gevent 的使用

  1. 介绍

    是一个基于协程(Coroutine)的 Python 网络编程库,它以 greenlet 为底层实现(解决了 greenlet 需要手动切换的痛点),能够自动检测 IO 操作并切换任务,从而实现高效的并发编程,尤其适合处理大量网络请求、IO 密集型任务(比如爬虫、接口测试、后端服务并发处理等)。

  2. 安装与卸载的命令

    • 安装 gevent

      (python -m) pip install gevent

    • 卸载 greenlet

      (python -m) pip uninstall gevent

    • 查看安装包

      (python -m)pip list

  3. 注意:文件命名不要和第三方模块活内置模块重名

  4. 使用:

    1. 导入gevent模块 import gevent

    2. gevent.spawn(函数名):创建协程对象

    3. gevent.sleep():耗时操作

    4. gevent.join():阻塞,等待某个协程执行结束

    5. gevent.joinall():等待所有协程对象都执行结束再退出,参数是一个协程对象列表

      import gevent def sing(): print("正在唱歌...") gevent.sleep(2) print("唱完歌了") def dance(): print("正在跳舞...") gevent.sleep(3) print("跳完舞了") if __name__ == "__main__": g1 = gevent.spawn(sing) g2 = gevent.spawn(dance) g1.join() g2.join() # 正在唱歌... # 正在跳舞... # 唱完歌了 # 跳完舞了
      import gevent def sing1(name): for i in range(3): gevent.sleep(1) print(f"{name}正在唱歌:第{i}次") if __name__ == "__main__": gevent.joinall([gevent.spawn(sing1,"小王"),gevent.spawn(sing1,"小李")]) # 小王正在唱歌:第0次 # 小李正在唱歌:第0次 # 小王正在唱歌:第1次 # 小李正在唱歌:第1次 # 小王正在唱歌:第2次 # 小李正在唱歌:第2次
    6. 其他补充:

      1. gevent.spawn() 返回的协程对象,通过 value 属性可以获取函数的返回值(需等待协程执行完成后才能获取)------g.value

      2. 使用 gevent.with_timeout() 可以为协程设置超时时间,超过时间未执行完成则抛出异常。------gevent.with_timeout(2, 函数名)

  5. Monkey补丁:

    拥有在模块运行时替换的功能

    1. monkey.patch_all() :(全量替换),将time.sleep()代码替换成gevent.sleep(),实现耗时操作。注意:一定放在被打补丁的前面

      from gevent import monkey import time,gevent monkey.patch_all() # 将time.sleep()代码替换成gevent.sleep(),实现耗时操作 def sing1(name): for i in range(3): time.sleep(1) print(f"{name}正在唱歌:第{i}次") if __name__ == "__main__": gevent.joinall([ gevent.spawn(sing1,"小李"), gevent.spawn(sing1,"小张") ]) # 小李正在唱歌:第0次 # 小张正在唱歌:第0次 # 小李正在唱歌:第1次 # 小张正在唱歌:第1次 # 小李正在唱歌:第2次 # 小张正在唱歌:第2次
    2. monkey.patch_time()(仅替换 time 模块)

    3. monkey.patch_socket()(仅替换 socket 模块)

    4. 适用场景:高并发网络请求(I/O 密集型典型场景)

      import gevent from gevent import monkey import requests import time # 第一步:先启用猴子补丁,再导入/使用其他库 monkey.patch_all() # 定义网络请求任务 def fetch_url(url, name): print(f"任务 {name} 开始请求:{url}") try: # requests 是同步库,但 socket 被猴子补丁改造,变为异步请求 response = requests.get(url, timeout=10) print(f"任务 {name} 请求成功,状态码:{response.status_code},响应长度:{len(response.text)}") except Exception as e: print(f"任务 {name} 请求失败:{str(e)}") # 要请求的多个 URL urls = [ "https://www.baidu.com", "https://www.taobao.com", "https://www.jd.com", "https://www.github.com", "https://www.stackoverflow.com" ] # 记录开始时间 start_time = time.time() # 1. 创建协程列表:为每个 URL 创建一个协程 greenlets = [] for idx, url in enumerate(urls, 1): g = gevent.spawn(fetch_url, url, f"URL-{idx}") greenlets.append(g) # 2. 等待所有协程执行完成 gevent.joinall(greenlets) # 记录结束时间 end_time = time.time() print(f"\n所有请求执行完毕,总耗时:{end_time - start_time:.2f} 秒")
  6. 注意事项:

    1. gevent 仅适用于 I/O 密集型任务(网络请求、文件读写、数据库操作等),不适用于 CPU 密集型任务(因为协程切换依赖 I/O 阻塞,CPU 密集型任务不会触发切换,会导致单个协程占用整个 CPU 资源)。

    2. 猴子补丁必须在导入其他模块之前执行,否则可能无法正常替换同步方法。

    3. gevent 是单线程的,所有协程都运行在同一个主线程中,因此无需考虑线程安全问题(但多进程场景仍需考虑)。

    4. 如果需要结合多进程处理 CPU 密集型任务,可以使用 gevent + multiprocessing 组合。

  7. 总结:

    • gevent 是基于协程的异步网络库,核心优势是轻量、高效,适合处理高并发 I/O 密集型任务。

    • 核心用法:通过 gevent.spawn() 创建协程、gevent.joinall() 等待协程执行,通过 monkey.patch_all() 实现同步代码的异步改造。

    • 关键注意:猴子补丁需提前执行,gevent 不适合 CPU 密集型任务,协程切换依赖 I/O 阻塞触发。

四、总结(进程、线程和协程)

  1. 线程是 CPU 调度的基本单位,进程是资源分配的基本单位

  2. 进程、线程和协程对比

    • 进程:切换需要的资源最大,效率最低

    • 线程:切换需要的资源一般,效率一般

    • 协程:切换需要的资源最小,效率高

  3. 多线程适合 IO 密集型操作 (文件操作、爬虫),多进程适合 CPU 密集型操作 (科学及计算、对视频进行高清解码、计算圆周率)

  4. 进程、线程、协程都是可以完成多任务的,可以根据自己实际开发的需要选择使用。

  5. 进程、线程、协程核心区别对照表

    对比维度

    进程

    线程

    协程

    资源分配

    资源分配的基本单位,拥有独立的内存空间、文件句柄等系统资源

    资源共享的基本单位,同一进程内的线程共享进程的内存和资源

    完全共享所在线程的资源,仅保存自身执行上下文(寄存器、栈)

    调度主体

    由操作系统内核调度,切换开销大(需保存 / 恢复进程上下文)

    由操作系统内核调度,切换开销中等(需保存 / 恢复线程上下文)

    由用户程序(如geventasyncio)调度,切换开销极小(仅需切换函数栈)

    切换开销

    最大(涉及内核态切换、内存映射刷新)

    中等(涉及内核态切换,但资源共享减少了数据拷贝)

    最小(用户态切换,无内核参与)

    并发能力

    受限于系统进程数上限,并发数较低(通常数百到数千)

    受限于系统线程数上限,并发数中等(通常数千到数万)

    极高(单线程内可支持数万到数百万协程)

    适用场景

    CPU 密集型任务(如科学计算、视频解码)、需要隔离资源的场景

    I/O 密集型任务(如网络请求、文件读写)、多任务并行计算

    高并发 I/O 密集型任务(如高并发爬虫、微服务接口)

    通信方式

    需依赖进程间通信(IPC),如管道、消息队列、共享内存

    直接读写共享变量(需加锁保证线程安全)

    直接读写变量(单线程内无锁问题,多线程需额外处理)

    系统开销

    高(每个进程独立占用内存和系统资源)

    中(线程共享进程资源,但仍需内核维护线程表)

    极低(仅用户态上下文,无内核额外开销)

    崩溃影响

    单个进程崩溃不影响其他进程(操作系统隔离)

    单个线程崩溃会导致整个进程崩溃(资源共享)

    单个协程崩溃仅影响所在线程,不影响进程

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/6/21 20:39:19

前后端分离+周边游平台系统|SpringBoot+Vue+MyBatis+MySQL完整源码+部署教程

摘要 随着互联网技术的快速发展和人们生活水平的不断提高,周边游成为现代人休闲娱乐的重要方式之一。传统的旅游平台往往采用前后端耦合的开发模式,导致系统扩展性差、维护成本高,难以满足用户个性化需求。此外,旅游行业的数字化转…

作者头像 李华
网站建设 2026/6/21 20:39:26

联合编程(加载单个工具,ini读写,图片读写,setting存储)

加载单个工具例子//保存到一个地方 让vs进行读取namespace 加载单个pma工具 {public partial class Form1 : Form{CogPMAlignTool pma;public Form1(){InitializeComponent();cogRecordDisplay1.Fit();}//窗体加载事件private void Form1_Load(object sender, EventArgs e){//加…

作者头像 李华
网站建设 2026/6/23 14:37:45

C++优先队列详解与仿函数应用

基本特性头文件&#xff1a;#include <queue>命名空间&#xff1a;std底层实现&#xff1a;通常基于堆&#xff08;heap&#xff09;数据结构实现默认行为&#xff1a;大顶堆&#xff08;最大元素优先出队&#xff09;时间复杂度&#xff1a;插入元素&#xff1a;O(log n…

作者头像 李华
网站建设 2026/6/21 17:35:09

智能升级,效率飞跃——建广数科AI助手赋能企业数字化转型

在数字化转型浪潮中&#xff0c;企业如何让内部运营更智能、更高效&#xff1f;建广数科自主开发的AI助手产品线&#xff0c;正以其精准的场景化服务与强大的技术能力&#xff0c;为这一问题提供了领先的解决方案。作为企业级智能服务平台&#xff0c;AI助手基于自然语言处理与…

作者头像 李华
网站建设 2026/6/21 8:43:58

docker 镜像导入导出

如果原文件是用 docker save 导出的&#xff0c;应该用 docker load 而非 docker import&#xff1a;# 错误方式&#xff08;丢失元数据&#xff09; docker import mysql.tar mysql8:8.4# 正确方式&#xff08;保留完整元数据&#xff09; docker load -i mysql.tar

作者头像 李华
网站建设 2026/6/21 20:38:55

SpringBoot+Vue 政府管理系统管理平台源码【适合毕设/课设/学习】Java+MySQL

摘要 随着信息技术的快速发展&#xff0c;政府管理系统的数字化和智能化已成为提升行政效率和服务质量的重要手段。传统的政府管理模式依赖于纸质文件和人工操作&#xff0c;不仅效率低下&#xff0c;还容易出现数据丢失和错误。为了解决这些问题&#xff0c;基于现代信息技术…

作者头像 李华