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 调度和事件循环,使协程可以自动切换

相关推荐
linzeyang1 小时前
Advent of Code 2025 挑战全手写代码 Day 8 - 游乐场
后端·python
超级种码1 小时前
JVM 字节码指令活用手册(基于 Java 17 SE 规范)
java·jvm·python
子午1 小时前
【垃圾识别系统】Python+TensorFlow+Django+人工智能+深度学习+卷积神经网络算法
人工智能·python·深度学习
CHANG_THE_WORLD1 小时前
Python 推导式详细教程
开发语言·python
ljh5746491192 小时前
用vscode怎么运行conda中的python环境
vscode·python·conda
秋邱2 小时前
AR 技术创新与商业化新方向:AI+AR 融合,抢占 2025 高潜力赛道
前端·人工智能·后端·python·html·restful
Stara05112 小时前
LangChain—大语言模型应用开发框架的体系化架构解析
python·langchain·llm·agent·提示工程·rag
只与明月听2 小时前
一个有趣的面试题
前端·后端·python
陌上倾城落蝶雨2 小时前
django基础命令
后端·python·django