Python 中的 IO 模型总结

一、IO 的分类与区别

类型 描述 举例
阻塞 IO IO 调用会一直等待数据就绪,期间无法做其他事 sock.recv()(默认)
非阻塞 IO IO 调用立即返回,数据没准备好时返回错误/空 sock.setblocking(False)
IO 多路复用 使用 select / poll / epoll 同时监听多个 IO 通道 select.select()
信号驱动 IO IO 就绪时,内核使用信号通知用户程序 少见
异步 IO(AIO) 用户发起 IO 后立即返回,内核完成 IO 后通知用户处理数据 asyncio / aiofiles 等


二、非阻塞 IO 的本质

  • 调用立即返回,不管数据是否就绪。

  • 用户需要反复轮询(或使用 select/epoll)来检查数据是否可读。

  • 使用场景:服务端处理高并发连接(如 socket 编程)。

python

复制编辑

sock.setblocking(False)
try:
    data = sock.recv(1024)
except BlockingIOError:
    # 数据没准备好,手动处理其他任务
    pass

三、异步 IO 的本质

异步 IO = 非阻塞 IO + 事件循环(Event Loop)

特点:

  • 使用 async/await 写法像同步代码一样简单

  • 事件循环自动调度任务,不需要手动轮询

  • 程序不会被 await 阻塞,而是挂起当前任务,继续执行其他任务

  • IO 一旦就绪,自动恢复挂起的协程

python

复制编辑

async def handle_client(reader, writer):
    data = await reader.read(1024)  # IO 等待期间可以去处理其他任务
    writer.write(b"ok")
    await writer.drain()

四、async 和 await 的作用

关键字 作用
async 声明一个协程函数,返回的是一个“协程对象”
await 暂停当前协程执行,等待一个异步操作完成,再继续执行

  • 其本质是:await 会调用目标对象的 __await__() 方法

  • 事件循环会将协程对象调度成任务,并在等待完成后自动唤醒


五、异步 IO 的底层流程(以 asyncio 为例)

  1. 程序调用 async def 定义的函数,得到协程对象

  2. 协程对象被 asyncio.run() 或事件循环调度为任务

  3. 遇到 await 时,挂起当前协程,执行其他任务

  4. IO 完成后,唤醒挂起的协程,继续执行

  5. 所有协程完成后,程序退出


六、阻塞 VS 非阻塞 VS 异步小结

模型 是否阻塞主线程 是否自动调度 写法复杂度 并发性能 使用场景
阻塞 IO  是  否     简单      差 简单客户端、脚本
非阻塞 IO  否  否(需手动轮询)     复杂      好 高并发服务、低层 socket 编程
异步 IO  否  是(事件循环)     简单    最优 Web 服务、爬虫、文件 IO 等


七、相关术语回顾

  • 事件循环(Event Loop):不断检查所有挂起任务/IO 状态,并在合适时机恢复执行。

  • 协程(Coroutine):可挂起、恢复的函数,比线程更轻量。

  • __await__:被 await 调用时执行的底层方法,返回一个迭代器对象,告诉事件循环“我等什么”。

  • 非阻塞:不会让程序停在某一步等待,而是能继续往下运行。

  • 异步:逻辑上的分离,IO 和计算不阻塞彼此。

Logo

有“AI”的1024 = 2048,欢迎大家加入2048 AI社区

更多推荐