Python asyncio 模块
Python3 学习笔记 · 整理自菜鸟教程
一句话
asyncio 用单线程事件循环实现异步并发,async def 定义协程、await 挂起等待,适合大量 I/O 等待场景。
要点
- 协程用
async def定义,调用它不会立即执行,只得到一个协程对象。 await挂起当前协程、让出控制权,等被等待的操作完成再继续。asyncio.run(main())是最简单的入口,创建事件循环、运行主协程、收尾关闭。- 并发跑多个协程:
create_task()建任务,或gather(*coros)一起等。 asyncio.sleep()是非阻塞睡眠,绝不要在里面用time.sleep()(会卡住整个循环)。- 同步/阻塞函数用
asyncio.to_thread(fn)丢到线程池,避免阻塞事件循环。 asyncio.wait_for(coro, timeout)限时;TimeoutError捕获超时。- 异步版同步原语:
asyncio.Lock、Event、Queue,用于协程间协作。 async for遍历异步迭代器,async with使用异步上下文管理器。Queue是协程间通信的推荐方式;asyncio.Queue。
语法 / 常用方法
| 语法/方法 | 说明 | 示例 |
|---|---|---|
async def f() |
定义协程函数 | async def main(): |
await coro |
等待协程/可等待对象 | await asyncio.sleep(1) |
asyncio.run(main()) |
运行主协程 | 程序入口 |
asyncio.create_task(coro) |
创建并调度任务 | 立刻开始并发 |
asyncio.gather(*aws) |
并发等待多个 | 返回结果列表 |
asyncio.sleep(sec) |
异步等待(不阻塞) | await asyncio.sleep(1) |
asyncio.wait_for(coro, timeout) |
限时等待 | 超时抛 TimeoutError |
asyncio.to_thread(fn, *args) |
阻塞函数丢线程 | 3.9+ |
asyncio.Queue() |
异步队列 | await q.put(x) |
asyncio.Lock() |
异步锁 | async with lock: |
asyncio.Event() |
异步事件 | await ev.wait() |
asyncio.as_completed(tasks) |
完成顺序产出 | 谁先完先处理 |
asyncio.TaskGroup() |
结构化并发(3.11+) | 统一管理子任务 |
async for x in aiter |
异步迭代 | 异步流 |
async with ctx |
异步上下文 | 异步资源管理 |
代码示例
1 | |
易错点
- 协程函数忘了
await或忘了用asyncio.run,只会得到一个协程对象,代码根本不执行(还会警告协程未 await)。 - 在协程里用
time.sleep()会阻塞整个事件循环,所有任务一起卡住,必须换await asyncio.sleep()。 - 同步阻塞调用(requests、文件大 IO)要用
to_thread包一层,否则退化成串行。 gather默认一个协程抛异常会向上扩散,需要return_exceptions=True才逐个收集结果。create_task建的任务如果一直不await也可能被垃圾回收取消,要显式持有引用或 await。- 事件循环里不能直接
await同步函数,await的对象必须是协程/任务/Future。 - Python 3.9 以下没有
asyncio.to_thread,用loop.run_in_executor替代。