先分清几个对象
学 asyncio 时最容易混的是 coroutine、Future、Task。先不用急着背定义,可以把它们放到一个实际调用里看:
python
async def fetch_user(user_id):
await asyncio.sleep(1)
return {"id": user_id}
task = asyncio.create_task(fetch_user(42))
user = await taskfetch_user(42) 只是创建了一个“以后可以执行的协程对象”。create_task() 把它交给事件循环。await task 等它完成并拿结果。
一句话版本
| 概念 | 它是什么 | 你什么时候接触它 |
|---|---|---|
| coroutine function | 用 async def 定义的函数 | 写业务逻辑时 |
| coroutine object | 调用 coroutine function 后得到的对象 | main()、worker() 的返回值 |
| awaitable | 可以被 await 的对象 | coroutine、Task、Future 都是常见 awaitable |
| Future | 一张“以后会有结果”的占位符 | 框架和库内部代码更常直接使用 |
| Task | 被事件循环调度的 coroutine | asyncio.create_task()、TaskGroup.create_task() |
| event loop | 负责安排谁先运行、谁继续等 | asyncio.run() 内部创建和关闭 |
| Handle | 被放到 ready 队列里的 callback | 读事件循环源码时会遇到 |
| Transport/Protocol | callback 风格的网络接口 | 框架作者更常用 |
| StreamReader/StreamWriter | 用 await 读写网络连接的接口 | 应用代码更常用 |
调用 async def 不会执行函数体
python
async def hello():
print("hello")
coro = hello()
print(coro)上面不会打印 hello。调用 hello() 只是拿到一个 coroutine object,函数体还没开始跑。要让它执行,你需要:
await hello(),由当前 Task 进入它。asyncio.run(hello()),创建事件循环并运行入口。asyncio.create_task(hello()),把它包装成 Task 并交给事件循环调度。
Task 是被调度的协程
没有 Task,coroutine object 就像一份还没排上号的工作。创建 Task 后,它才进入事件循环的队列,后面会被安排执行。
Future 是结果容器
Future 不负责计算。它只记录一件事:结果现在有没有准备好。
text
pending -> done(result)
pending -> done(exception)
pending -> cancelledTask 也会有结果、异常和取消状态,所以它看起来很像 Future。差别是:
- Future 只等别人把结果填进来。
- Task 包着一个 coroutine,会自己一步步把它跑下去。
事件循环负责什么
事件循环不是魔法。它反复做几件很普通的事:
| 职责 | 典型 API | 内部数据结构 |
|---|---|---|
| 立刻调度 callback | loop.call_soon() | ready 队列 |
| 延迟调度 callback | loop.call_later()、asyncio.sleep() | 定时器堆 |
| 继续运行 Task | create_task() | Task 的 _step callback |
| 等待 I/O 就绪 | streams、socket、subprocess | selector 或平台事件机制 |
| 管理生命周期 | asyncio.run()、Runner | 创建、运行、关闭 loop |
await 做了什么
从使用者角度看:
python
value = await something()意思是:如果 something() 还没结果,当前协程先停下。事件循环这时可以去处理别的任务。等 something() 有结果后,再回到这一行继续。
从实现角度看,大致是这样:
写应用时先用哪些 API
写应用时,先用这些:
asyncio.run()asyncio.create_task()asyncio.TaskGroupasyncio.gather()asyncio.timeout()asyncio.Queueasyncio.open_connection()asyncio.start_server()
下面这些更多是库和框架会碰到,入门时不用急:
loop.call_soon()loop.call_later()loop.create_future()loop.add_reader()loop.create_connection()- Transport/Protocol
如果你只是写业务代码,通常不需要手动调用 loop.call_soon() 或 loop.create_future()。等你要把一个 callback 风格的网络库接进 asyncio,再看这些低层 API 会更容易理解。
小练习
回答下面问题:
async def f()和f()分别是什么?create_task(f())做了哪两件事?- Future 和 Task 的共同点是什么,差别是什么?
- 为什么
time.sleep()放进async def会堵住所有 Task?