Python 异步编程与 Gevent 实战指南

在 Python 中,异步编程和协程技术已经广泛应用于网络爬虫、IoT 设备数据处理、Web 服务等场景。本文将从基础原理、gevent/greenlet、monkey.patch_all 的作用、阻塞函数调度问题到实际应用案例,全面讲解 Python 异步实践。


一、Python 异步基础

Python 自 3.4 版本引入了 asyncio,3.5 版本开始支持 async / await 语法,用于处理 IO 密集型任务。

python 复制代码
import asyncio

async def hello():
    print("Hello")
    await asyncio.sleep(1)
    print("World")

asyncio.run(hello())

注意:asyncio 只能调度异步函数或 IO,可以通过 await 让事件循环切换。纯阻塞函数无法被 asyncio 调度。


二、Greenlet 与 Gevent

2.1 Greenlet

  • Greenlet 是一个 轻量级协程库

  • 它可以在 Python 函数之间 手动切换执行

  • 特点

    • 只负责"切换执行权",不做 IO 调度。
    • 非阻塞函数之间切换非常快。
python 复制代码
from greenlet import greenlet

def task1():
    print("Task 1 start")
    gr2.switch()
    print("Task 1 end")

def task2():
    print("Task 2 start")
    gr1.switch()
    print("Task 2 end")

gr1 = greenlet(task1)
gr2 = greenlet(task2)
gr1.switch()

2.2 Gevent

  • Gevent 是 基于 Greenlet 的协程框架

  • 它把 Greenlet + IO 自动调度 结合起来:

    • 对标准库中的阻塞 IO(socket、time.sleep 等)进行 monkey patch
    • 当一个 greenlet 执行阻塞 IO 时,自动切换到其他 greenlet。
python 复制代码
import gevent
from gevent import monkey
monkey.patch_all()  # 改写阻塞 IO

def task1():
    print("Task 1 start")
    gevent.sleep(1)
    print("Task 1 end")

def task2():
    print("Task 2 start")
    gevent.sleep(1)
    print("Task 2 end")

gevent.joinall([
    gevent.spawn(task1),
    gevent.spawn(task2)
])

2.3 Greenlet 与 Gevent 的关系

组件 功能
Greenlet 轻量级协程,只切换执行权,不调度 IO
Gevent 基于 Greenlet,自动调度 IO + 事件循环 + monkey patch
monkey.patch_all() 改写标准库阻塞函数,让 Gevent 可以调度

一句话理解:Greenlet 是骨架,Gevent 给骨架加了心跳(IO 调度和事件循环)。


三、monkey.patch_all() 原理

  • 对标准库的阻塞函数(如 socket、ssl、time.sleep、threading)进行替换
  • 替换后的函数会在阻塞时让出控制权,切换到其他 greenlet
  • 适用于 IO 密集型操作,无法影响纯 CPU 密集型或 C 扩展阻塞函数

示例:

python 复制代码
from gevent import monkey
monkey.patch_all()
import socket

s = socket.socket()  # 实际上被 gevent.socket 替换

四、阻塞函数与异步调度

Python 中 C 扩展阻塞函数(如 pandas.read_excel()、numpy 数值计算)不会触发协程调度。

python 复制代码
import pandas as pd

df = pd.read_excel("data.xlsx")  # 阻塞操作,无法被 asyncio 或 gevent 调度

解决方案:使用线程池或进程池

python 复制代码
import pandas as pd
import asyncio
from concurrent.futures import ThreadPoolExecutor

executor = ThreadPoolExecutor()

async def read_excel_async(path):
    loop = asyncio.get_event_loop()
    return await loop.run_in_executor(executor, pd.read_excel, path)

async def main():
    df = await read_excel_async("data.xlsx")
    print(df)

asyncio.run(main())

优点:事件循环不被阻塞,pandas 执行在独立线程中。


五、总结

  1. asyncio 适用于 IO 协程,无法调度阻塞 C 扩展
  2. gevent 通过 monkey.patch_all() 改写标准库 IO 实现协作式调度
  3. CPU 密集型或 C 扩展阻塞函数需放线程池/进程池
  4. pandas.read_excel 可通过 run_in_executor 异步读取
  5. Greenlet 是 Gevent 的核心协程骨架,Gevent 提供 IO 调度和事件循环,使协程可以自动切换

相关推荐
麻雀飞吧1 分钟前
2026年AI量化开发,先跑通小流程再加复杂功能
人工智能·python
daphne odera�16 分钟前
PyCharm 中 Codex 插件启动失败:unknown variant default 的解决方法
python·chatgpt·pycharm
nbu04william44 分钟前
Deepseek-api省token的用法
python·大模型·token·deepseek
测试老哥1 小时前
Pytest自动化测试详解
自动化测试·软件测试·python·测试工具·测试用例·pytest·接口测试
坚持学习前端日记1 小时前
国产化适配全流程适配英伟达本地开发
人工智能·python
源图客2 小时前
云途物流API开发-鉴权认证(Java)
java·网络·python
三川6983 小时前
Tkinter库的学习记录06-变量类别
python
wenying_443237443 小时前
Python脚本—1.提取压缩包中的pdf文件
python·ai编程
_Jimmy_3 小时前
SQLAlchemy 复杂 SQL 执行指南与模板库
python·sql
六个九十度3 小时前
用python脚本访问被网关隔离的嵌入式设备
开发语言·python