计算机原理101:什么是协程

Posted by FridayLi on April 1, 2025

计算机原理101: 什么是协程

《计算机底层的秘密》读书笔记 + GPT问答整理

1. 什么是协程?

描述

与普通函数不同的是, 协程能知道自己上一次执行到了哪里。有暂停和恢复的能力。

协程最重要的作用是让程序员以同步的方式来进行异步编程。

2. 协程是如何实现的?

描述

在堆区中申请一块内存用来存放协程的运行时栈帧信息。协程的切换、调度完全发生在用户态, 不需要操作系统介入。

协程相关数据 存储位置
协程的代码逻辑 程序的代码区
当前执行位置(指令指针) 堆(或结构体)
局部变量/栈帧副本
调用上下文(如await点)
栈帧(部分语言) 编译器可能模拟栈结构到堆

🎯 举例: Python 的 async def 协程会编译为一个生成器对象(coroutine object),状态保存在对象内部的 frame 和 stack 属性中——这些都在堆上。

C# 的 async 方法会被编译为一个状态机类,局部变量、await 点等保存在该状态机的字段中——也在堆上。

Python 协程是基于生成器协议实现的状态机,其挂起点通过 await 保存状态在堆中的协程对象中,恢复由事件循环控制,通过 .send() 继续执行。

3. 协程是怎么休眠和恢复的?

协程的暂停和恢复核心在于状态机机制(state machine) 🌀 步骤简化:

  1. 编译时构建状态机 协程函数会被编译为一个状态机,每个 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;
}
  1. 暂停:遇到 await 或 yield
    • 当前执行状态(如状态编号、局部变量)被保存到状态对象中(堆上);
    • 控制权返回给调度器(如事件循环);
    • 所在线程可以去执行别的任务。
  2. 恢复: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+) 异步协程 引入任务调度系统和异步函数