计算机原理101: 什么是协程
《计算机底层的秘密》读书笔记 + GPT问答整理
1. 什么是协程?
与普通函数不同的是, 协程能知道自己上一次执行到了哪里。有暂停和恢复的能力。
协程最重要的作用是让程序员以同步的方式来进行异步编程。
2. 协程是如何实现的?
在堆区中申请一块内存用来存放协程的运行时栈帧信息。协程的切换、调度完全发生在用户态, 不需要操作系统介入。
协程相关数据 | 存储位置 |
---|---|
协程的代码逻辑 | 程序的代码区 |
当前执行位置(指令指针) | 堆(或结构体) |
局部变量/栈帧副本 | 堆 |
调用上下文(如await点) | 堆 |
栈帧(部分语言) | 编译器可能模拟栈结构到堆 |
🎯 举例: Python 的 async def 协程会编译为一个生成器对象(coroutine object),状态保存在对象内部的 frame 和 stack 属性中——这些都在堆上。
C# 的 async 方法会被编译为一个状态机类,局部变量、await 点等保存在该状态机的字段中——也在堆上。
Python 协程是基于生成器协议实现的状态机,其挂起点通过 await 保存状态在堆中的协程对象中,恢复由事件循环控制,通过 .send() 继续执行。
3. 协程是怎么休眠和恢复的?
协程的暂停和恢复核心在于状态机机制(state machine) 🌀 步骤简化:
- 编译时构建状态机 协程函数会被编译为一个状态机,每个 await 是一个“状态切换点”。
例如,下面的 C# 协程:
async Task ExampleAsync() {
await A(); // 状态0
await B(); // 状态1
await C(); // 状态2
}
会被转成如下伪状态机:
switch (state) {
case 0:
await A(); state = 1; return;
case 1:
await B(); state = 2; return;
case 2:
await C(); state = -1; return;
}
- 暂停:遇到 await 或 yield
- 当前执行状态(如状态编号、局部变量)被保存到状态对象中(堆上);
- 控制权返回给调度器(如事件循环);
- 所在线程可以去执行别的任务。
- 恢复:await 的异步操作完成时
- 调度器调用状态机的 MoveNext() 或 resume();
- 从保存的状态继续执行,仿佛从 await 后一行继续运行。
关键点 | 说明 |
---|---|
状态存储位置 | 在堆上,封装为对象(如 Python 的 coroutine,C# 的状态机类) |
暂停机制 | 将当前执行点、局部变量等打包保存 |
恢复机制 | 调度器在任务就绪时调用 resume / MoveNext |
栈结构如何处理 | 编译器将栈转为堆中结构,以便跨时间点保留状态 |
Python和C#中的协程调度
特性 | C# (.NET) | Python (asyncio) |
---|---|---|
调度器角色 | TaskScheduler + 状态机 + SynchronizationContext |
asyncio 的事件循环(EventLoop ) |
如何恢复协程 | 调用状态机的 MoveNext() |
调用 coroutine.send() 或内部 resume |
主动控制调度器 | 可以自定义 TaskScheduler |
可以自定义 EventLoop 或使用 loop.create_task |
是否多线程 | 依赖调度器(可在 UI 或线程池) | 默认单线程(事件驱动) |
📌 所以:C# 中的“调度器”可以理解为 await 背后由编译器和 .NET runtime 编排的 TaskScheduler + 状态机 + 同步上下文组合
📌 所以:Python 中的调度器是 asyncio 中的 事件循环对象,通常通过 asyncio.run() 启动并控制。
4. 协程和线程的区别是什么?
特性 | 协程 | 线程 |
---|---|---|
栈帧保留方式 | 状态转为对象存堆上 | 真实调用栈保存在内核栈上 |
挂起恢复 | 由状态机控制 resume | 由操作系统调度器控制 |
内存开销 | 小,1 个协程只需几 KB 内存 | 大,线程通常要分配 1MB 栈空间 |
5. 协程和I/O的关系
协程是一种结构化的方式,让我们可以在 I/O 操作期间“挂起”执行、释放线程资源,并在 I/O 完成后继续执行,而不会阻塞线程。
等 I/O 完成 → 程序自动“挂起” → 当前线程可以去干别的事 → I/O 完成后自动“恢复”
协程的最大价值就是用在 I/O 密集型任务 上,尤其是网络请求、文件操作、数据库访问等慢操作。
项目 | C# | Python |
---|---|---|
协程关键词 | async , await |
async def , await |
异步 I/O 库 | HttpClient , StreamReader , 等 |
aiohttp , aiofiles , asyncpg , 等 |
调度机制 | Task-based 状态机 + 同步上下文 | asyncio 的事件循环 |
异步本质 | I/O 操作返回 Task<T> ,状态由调度器处理 |
I/O 操作返回 Future ,由事件循环控制 |
线程利用 | I/O 期间释放线程,让出 CPU | I/O 期间挂起协程,让出执行流 |
6. 都哪些语言实现了协程?
语言 | 协程关键字/方式 | 协程类型 | 说明 |
---|---|---|---|
Python | async def / await |
异步协程(基于生成器) | 使用 asyncio 提供事件循环,支持高并发 I/O |
C# | async / await |
异步协程(基于状态机) | 编译器自动生成状态机,结合 Task<T> |
Kotlin | suspend / coroutineScope |
协程是语言核心 | 支持结构化并发、挂起函数,不阻塞线程 |
Go | go 关键字(goroutine) |
轻量级协程 | 原生支持并发调度器,非抢占式多协程模型 |
Lua | coroutine.create/yield/resume |
对称协程 | 轻量级实现,可手动切换控制权 |
Rust | async / .await (稳定后支持) |
异步协程(基于 Future) | 零成本抽象,用 tokio / async-std 运行 |
JavaScript | async / await (ES2017+) |
异步协程(基于 Promise) | 常用于浏览器和 Node.js 异步控制 |
Swift | async / await (Swift 5.5+) |
异步协程 | 引入任务调度系统和异步函数 |