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

相关推荐
六bring个六10 小时前
C++20协程
c++20·协程
橙露10 小时前
时间序列分析实战:用 Python 实现股票价格预测与风险评估
人工智能·python·机器学习
神云瑟瑟10 小时前
看langchain理解python中的链式调用
python·langchain·链式调用
栈与堆10 小时前
LeetCode 21 - 合并两个有序链表
java·数据结构·python·算法·leetcode·链表·rust
CCPC不拿奖不改名10 小时前
循环神经网络RNN:整数索引→稠密向量(嵌入层 / Embedding)详解
人工智能·python·rnn·深度学习·神经网络·自然语言处理·embedding
鹤入云霄10 小时前
基于Python的空气质量监测系统
python
长行12 小时前
Python|Windows 安装 DeepSpeed 安装方法及报错 Unable to pre-compile async_io 处理
windows·python·deepspeed
百锦再12 小时前
python之路并不一马平川:带你踩坑Pandas
开发语言·python·pandas·pip·requests·tools·mircro
Python之栈12 小时前
5款拖拽式Python GUI生成器助你快速打造炫酷界面
python
灏瀚星空12 小时前
基于 Python 与 GitHub,打造个人专属本地化思维导图工具全流程方案(上)
开发语言·人工智能·经验分享·笔记·python·个人开发·visual studio