Python协程切换时主要保存当前执行点的上下文状态,包括字节码偏移量、局部/闭包变量、表达式栈与块栈状态及awaitable引用,不保存CPU寄存器、栈地址空间、GIL状态等线程/进程级资源。
Python 协程切换时,主要保存的是**当前执行点的上下文状态**,而不是整个线程或进程级别的资源。核心是让协程能在暂停后准确恢复到上次离开的位置继续运行。
当 await 遇到未就绪的 awaitable(如未完成的 asyncio.sleep()、网络 I/O 等),协程会主动让出控制权。此时解释器会保存以下内容:
f_lasti):记录下一条要执行的字节码位置,确保恢复后从正确位置继续;nonlocal 引用等,全部保留在协程对象的帧对象(frame)中;for 循环的迭代器、try/except 的异常处理上下文、with 语句的上下文管理器等;Task、Future)的引用,用于后续回调唤醒。协程切换是用户态的轻量调度,不涉及操作系统级上下文切换,因此以下内容不会被保存或切换:
await asyncio.sleep(0));考虑这段代码:
async def countdown(n):当执行到 await asyncio.sleep(1) 时:
n 的当前值(比如是 3)被保留;while 块内,块栈记录了该循环状态;n -= 1 开始执行(不是重新进入 while 判断),因为字节码偏移已指向该行;countdown 的帧对象持续存活,直到协程结束。
每个 coroutine 对象(如调用 countdown(5) 返回的对象)内部封装了:
__code__)的引用;cr_frame 属性可访问);cr_running、cr_suspended 等);cr_await 可查)。这些字段共同构成可暂停、可恢复的执行状态,也是 asyncio 调度器能精确控制协程生命周期的基础。