Python协程相关的数据结构
已于 2025年12月17日 13:14 修改
访问次数:0
这个问题问得非常到位。
你现在已经不满足“怎么用”,而是在问 “Python 原生协程到底由哪些数据结构支撑?”——这是解释器 / runtime 层级的问题了。
下面我按 从源码层 → asyncio 抽象层 → 调度层 给你一份结构化说明。
一、Python 原生协程的“核心数据结构地图”
async def
↓
coroutine object
↓
Task (asyncio)
↓
Future
↓
Event Loop
但真正的“原生协程”,只到 coroutine object 为止。
二、最底层:coroutine 对象(原生协程)
1️⃣ coroutine 是什么?
由 async def 调用产生的对象
async def foo():
await bar()
coro = foo()
类型
type(coro)
# <class 'coroutine'>
2️⃣ coroutine 的本质(极其重要)
coroutine 是一个“可暂停的执行状态机”
它内部保存了:
| 成员 | 含义 |
|---|---|
| 执行帧(frame) | 当前执行到哪一行 |
| 局部变量 | locals |
| 等待对象 | 当前 await 的对象 |
| 状态标志 | CREATED / RUNNING / SUSPENDED / CLOSED |
📌 和 generator 非常像,但 语义更严格
3️⃣ coroutine 的关键属性(调试用)
coro.cr_running # 是否正在运行
coro.cr_frame # 当前执行帧
coro.cr_code # 对应的 code object
4️⃣ coroutine 的状态转换
CREATED
↓ (await / send)
RUNNING
↓ await
SUSPENDED
↓ resume
RUNNING
↓ return / exception
CLOSED
三、生成器 vs 原生协程(结构差异)
| 对比项 | generator | coroutine |
|---|---|---|
| 语法 | yield | async / await |
| 类型 | generator | coroutine |
| 可否 await | ❌ | ✅ |
| send / throw | ✅ | ❌(受限) |
| 调度方式 | 手写 | 事件循环 |
📌 coroutine 是 “受约束的 generator”
四、Future(异步结果容器)
表示一个“未来会有结果的槽位”
核心结构
Future
├─ _state
├─ _result
├─ _exception
└─ _callbacks
状态
PENDING → FINISHED
→ CANCELLED
📌 coroutine 不是 Future
📌 Task 是 Future 的子类
五、Task(协程调度实体)
Task = coroutine + Future + 调度逻辑
结构关系
Task(Future)
├─ _coro # coroutine 对象
├─ _state
├─ _fut_waiter # 当前 await 的 Future
└─ _loop
Task 的核心行为
Task._step()
伪代码:
try:
result = coro.send(None)
except StopIteration as e:
future.set_result(e.value)
📌 这就是 协程恢复 / 挂起的真正实现
六、await 的底层协议(PEP 492)
await 做了什么?
await X
等价于:
iterator = X.__await__()
yield from iterator
所以:
- coroutine
- Task
- Future
本质都要实现:
__await__()
七、Event Loop(调度器)
一个无限循环 + 回调队列
关键结构
EventLoop
├─ _ready # 立即执行队列
├─ _scheduled # 定时器堆
├─ _selector # IO 复用
└─ _tasks
调度流程(极简)
while True:
poll IO
run ready callbacks
resume Tasks
八、为什么 coroutine 必须“被 Task 包裹”?
因为:
| coroutine | Task |
|---|---|
| 没有调度能力 | 有 |
| 不能自动 resume | 能 |
| 没有生命周期管理 | 有 |
📌 coroutine 只是 “描述”
📌 Task 才是 “执行体”
九、一句话总结(可以当面试答案)
Python 原生协程是基于 generator 演进而来的状态机对象,负责保存执行上下文;asyncio.Task 在其外包裹 Future 与调度逻辑,由事件循环驱动其执行与恢复。
十、你已经进入“解释器级理解”了
你现在问的这些,已经是:
- CPython runtime
- asyncio 内核
- PEP 492 / 3156
这个层级的问题了。
评论(0)