Files
Endorphin-Document/appendices/language-runtime-lifecycles.md
T

38 KiB
Raw Blame History

12 门语言的执行模型与生命周期

本文比较代码如何进入执行、运行状态如何保存、等待后如何继续,以及资源如何结束存续。逐词含义见关键词手册;请求、场景和界面的完整路径见框架如何接管代码执行。本文是外部语言参考,不定义 Endorphin 的新语法或默认调度规则。

执行模型资料核对日期:2026-10-13。关键词版本基线沿用原手册;这里分析指定实现的机制,不宣称覆盖所有实现或版本。Python 使用 CPython 3.14;Java 使用 Java SE 26 的规范;C 使用 C23 公开草案;C++ 使用公开工作草案的相关条款,不将其所有新增能力归入 C++23。Go runtime、React 源码和其他持续更新的官方资料,只作为所注明实现的证据。Kotlin 已发布规范中的协程说明用于解释机制,不作为 Kotlin 2.4 全量版本审计;Julia 使用官方 v1 文档,不将实现细节冻结为 1.11 的全部承诺。

阅读路径

先阅读统一模板,再选一门熟悉的语言。建立普通调用的认识后,比较协程和任务,最后进入框架案例。

语言 分析入口 框架或宿主案例
Python CPython、执行帧与协程 FastAPI
JavaScript 执行上下文、Promise 与宿主 Node/Chromium、React
TypeScript 类型处理与 JavaScript 执行 TSX 与 React
Java JVM、类初始化与线程 Spring Boot
C 进程入口与存储期 普通进程
C++ 对象生命周期与协程帧 库或宿主提供调度
C# .NET、Task 与状态机 Unity
Go goroutine、channel 与 runtime runtime 调度
Rust 所有权、Future 与 executor 异步运行库提供调度
Swift ARC、Task 与 executor 并发运行时及应用宿主
Kotlin Continuation、Job 与 dispatcher 协程库及目标平台
Julia 方法、Task 与调度 Julia 运行时

统一分析模板

常用结构名称

名称 本文中的含义
frame/执行帧 一次调用或执行的参数、局部状态与继续关系;具体布局由实现决定
continuation/继续关系 暂停后该怎样继续计算,可以用保存状态、函数或其他结构表达
状态机 将执行进度表示为不同状态及迁移,并保存跨暂停需要的数据
scheduler/executor 接收工作并安排推进的设施;不等于所有计算都由它解释执行
awaiter/等待协议 检查是否完成,并在需要等待时建立继续关系的机制
Task/Job 具体运行环境管理的活动或工作;同名设施在不同语言中并不等价
Future/Promise 表达结果及其后续取得或推进协议的设施;不等于专属线程
GC/ARC/RAII 分别强调可达性回收、引用计数管理、将资源管理关联到对象初始化与析构;具体保证见各语言
JIT/AOT 分别表示运行时编译与提前编译;它们回答代码怎样准备,不直接定义哪个业务事件触发调用

生命周期属于一个具体实体

“程序的生命周期”通常包含多个不同尺度的生命周期。进程仍然存在时,函数调用可以已经返回;组件仍然存在时,某次渲染可以已经完成;任务暂停时,其上下文仍然存在。分析时先明确实体,再讨论状态。

实体 典型开始 典型结束 不能混淆的关系
进程或应用 启动入口被接管 正常退出、关闭或终止 应用存续不意味着用户代码持续执行
REPL 会话 创建输入与求值上下文 会话关闭 一次输入完成后,绑定仍可保留
函数定义 定义被创建、加载或准备 不再需要或所属环境结束 定义存在不意味着函数体已经执行
一次调用 控制流进入函数 返回或异常离开 同一函数可以对应很多次独立调用
协程或任务 创建状态及进入执行 完成、失败或取消处理结束 创建、调度、首次运行可能是不同动作
组件或订阅 挂载、启用或注册 卸载、禁用或取消订阅 一次回调返回通常不销毁组件或订阅
数据对象或资源 构造、分配或获得资源 释放、销毁或回收 内存回收与文件、连接关闭分别处理

定义、上下文、管理结构分别保存信息

下面是比较用的概念分类,不要求每个实现都存在三个同名结构。

结构组 保存的信息 常见实例
代码与定义 可执行逻辑、类型、模块、依赖及捕获环境 code object、函数对象、方法元数据、机器码、模块记录
一次执行的上下文 输入、局部绑定、进度、等待及结果关系 栈帧、协程帧、Continuation、goroutine 栈
调度与管理结构 就绪工作、等待者、触发关联及所有者 就绪队列、I/O 注册表、Task、Job、组件树、订阅表

执行上下文需要能够支持下列职责,但信息可以分散保存、被编译器消除,或由外部宿主提供。

信息 作用 典型承载方式
本次采用的代码与依赖 确定执行哪份逻辑 方法元数据、函数引用、代码地址与环境
输入与局部绑定 保存这次计算的数据 局部槽位、寄存器、栈、状态机字段
当前进度 决定继续执行的位置 指令位置、返回地址、状态机标签
执行状态 区分运行、等待和结束 Task/Job 状态、调度器记录
等待条件与继续位置 外部条件满足后能够继续 awaiter、Waker、等待队列、Continuation
调用关系与结果接收者 交付返回值或失败 调用栈、Promise reaction、父任务、回调
执行身份与可用能力 判断操作的归属与授权 进程权限、请求上下文、宿主能力;不是所有语言执行帧的原生字段

对象地址只在所在进程的地址空间里有意义。网络发送的是协议表示;接收端创建或更新自己的对象。跨端关联必须另有身份和通信约定,不能从局部变量名字推断远端内存位置。Endorphin 对平台对象的说明见资源访问与呈现。

触发、调度与执行承担不同职责

触发关系确定发生某个条件后应进入哪段逻辑。调度确定工作何时获得执行机会。执行引擎或原生代码推进具体计算。普通同步调用可以直接进入函数,不经过应用任务队列;应用事件循环之外,还可能有操作系统线程调度。

flowchart LR
    A[启动、输入、事件或条件满足] --> B[确定入口与输入]
    B --> C[直接调用或提交可运行工作]
    C --> D[执行引擎或原生代码推进]
    D --> E{本次执行的结果}
    E --> F[返回或失败:交付结果并清理]
    E --> G[等待:保存上下文与等待关系]
    G --> H[条件满足:重新获得执行机会]
    H --> D

图中是概念路径,不能据此推出统一队列、抢占策略或状态枚举。回调注册通常保存关联,注册函数自身也可能执行初始化逻辑;必须查具体 API。等待结果的任务也不一定需要不断主动轮询。

比较可暂停活动时,可以使用下面的分析状态;它们不是各语言共有的原生枚举。

分析状态 进入条件 后续处理
已创建 逻辑与初始上下文已经关联 可能尚未调度或运行,例如新建的 Julia Task
可运行 有条件推进,但尚未获得执行机会 由相应调度策略选中
运行中 正在推进代码 继续、等待、完成或失败
等待中 当前不能继续,已保存状态及等待关系 条件满足后获得继续机会
已完成/失败 逻辑得到结果或以错误结束 交付结果,完成相应清理;结果句柄仍可存续

取消请求可以在运行或等待期间出现;活动接受取消、执行清理并终止之前,不能把“请求取消”直接当成“已结束”。组件的启用、提交或卸载也不是上表的同一套状态。

恢复、重算、释放各有含义

恢复是在保留的上下文中继续;重算是开始另一次求值。React 再次调用组件属于重新计算,Rust executor 再次 poll 同一个 Future 属于推进保存的状态。取消通常是请求停止及处理清理,不自动撤销已经发生的写入。GC 判断内存对象是否可回收,不代替所有业务资源的关闭。

1. Python

入口与执行者。 CPython 执行脚本、模块或 REPL 输入中的代码块。模块顶层会执行;执行 def 创建函数并处理默认值、装饰器等,普通函数体在调用时运行。class 的类体在定义类时执行。首次导入通常执行模块初始化,后续导入通常使用模块缓存。Python 规范中的执行帧概念与 CPython 具体实现应分开。Python 执行模型、复合语句

结构 保存什么 生命周期及归属
code object 编译后的代码及常量、名称等元数据 可被函数引用和多次执行;不包含某次调用的全部数据
function object code、全局环境、默认值、闭包等 定义执行时创建,通常跨多次调用存续
执行帧及解释器内部状态 局部绑定、进度、调用关系 一次执行;可因生成器、协程或外部引用保留
coroutine/generator、asyncio Task 可暂停状态;Task 进一步跟踪结果、取消和调度 调用者或事件循环持有,完成不等于引用立即消失
PyObject 及具体对象结构 CPython 对象的类型及具体内容 对象存续期间;不是“当前正在执行”的标记

PyObject 是 CPython 对象表示的基础,不是在每次运行字节码时才统一创建。启动初始化、执行定义、构造值都可能产生对象;code、function 与一次执行的帧也不同。对象 C API 说明、函数对象 C API、代码对象 C API

具体帧内部表示不等于每次调用立即创建一个可见 PyFrameObject。CPython 的帧优化允许在需要时才物化相应 Python 帧对象;不能用调试器看见的对象反推每次调用的全部分配行为。按需创建 Python 帧

进入与等待。 普通函数调用立即进入函数体;生成器调用先取得生成器,迭代推进它。调用 async def 先得到 coroutine,随后由 await、asyncio.run 或 Task 等方式驱动。Task 的急切执行配置会改变首次推进时机,不能一律理解为下一轮循环才开始。asyncio 协程与任务

下面是 CPython/asyncio 的独立示例,预期输出顺序为 created、body、42,不是运行日志。

import asyncio

async def price():
    print("body")
    await asyncio.sleep(0)
    return 42

async def main():
    pending = price()
    print("created")
    print(await pending)

asyncio.run(main())

结束与关键词。 return 和异常结束调用;yield 保存生成器继续状态,await 表达等待。with、finally 用于资源退出处理;del 删除绑定等,不保证对象立即销毁。CPython 的引用计数及循环 GC 是实现机制,不是所有 Python 实现必须照搬的机制。Python 数据模型。请求入口另由 FastAPI 与 ASGI组织。

2. JavaScript

入口与执行者。 宿主启动脚本或模块求值,再通过普通调用和回调进入函数。V8 是 Node 与 Chromium 使用的执行引擎;网络、DOM、定时器和完整宿主事件循环分别由宿主提供。ECMAScript 定义执行上下文与环境记录,它们是规范模型,不要求 V8 使用相同名称或布局。ECMAScript 执行上下文

结构 保存什么 生命周期及归属
函数、脚本/模块及环境 逻辑、绑定和闭包捕获 引擎管理;模块求值与函数调用分别发生
执行上下文栈 当前函数、环境及执行进度 活跃的同步调用;闭包环境可继续存续
Promise 与 reaction 完成状态、结果及后续处理 Promise 解决后仍可被引用,不等于线程或整个执行帧
生成器/异步继续状态 暂停后的局部信息与继续关系 引擎保存并按相应协议恢复
宿主任务与微任务管理 可运行的回调或 job 队列、检查点和执行时机受宿主规范约束

进入与等待。 普通函数立即运行;async 函数调用会运行同步前缀,再返回 Promise,await 后续通过异步继续机制推进;生成器由 next() 等驱动。Promise 构造器的 executor 与 .then() 注册的后续处理不是同一个时机。ECMAScript 函数定义、Promise 对象

下面是普通脚本示例,预期输出 start、outside、resume。

async function price() {
  console.log("start");
  await Promise.resolve();
  console.log("resume");
}
price();
console.log("outside");

调度与结束。 浏览器在规定位置执行微任务检查点,清空过程中还可加入新微任务;渲染发生在符合条件的渲染机会,不保证每个任务后立即绘制。Node 另有 libuv 阶段和 process.nextTick 规则。return、throw、try/finally 描述调用及清理,GC 不能代替解除事件监听或业务资源关闭。HTML 事件循环、Node 事件循环

3. TypeScript

入口与执行者。 通常由工具处理 TypeScript,输出或交给后续工具产生 JavaScript,再由所选宿主执行。类型检查不是持续坐在 JavaScript 和 V8 之间的另一台解释器。type、interface 等类型信息通常被擦除;部分构造如普通 enum、namespace 可以生成运行时代码,不能把所有 TS 内容都理解为无运行效果。类型擦除、枚举、namespace

结构 保存什么 生命周期及归属
编译工具的 AST、符号与类型信息 源码关系、类型检查及转换所需信息 构建或编辑会话;通常不进入应用运行堆
生成的 JS 模块、函数和对象 真正运行的逻辑与数据 采用 JavaScript 的运行模型
source map 源码与生成代码位置的对应 调试工具使用;不负责调度
框架自己的组件、任务或请求记录 React、服务端等管理状态 由对应框架承担,不是 TypeScript 类型对象

调用与等待。 async、await 的可观察调用行为依照目标 JavaScript 及工具生成的支持代码。TS 的返回类型 Promise<number> 描述结果契约,不因类型标注自行启动任务。值导入可能带来模块初始化;纯类型导入不为类型本身创建业务运行对象。

TSX 与结束。 JSX 可以由 TypeScript 转换,也可以保留给其他工具处理;生成代码调用 React 等库,然后才进入组件管理。清理、事件循环和对象回收归实际宿主与库。完整路径见 React 构建、浏览器执行及 SSR。TypeScript JSX 模式

4. Java

入口与执行者。 启动器选择入口,JVM 完成必要的类加载、链接、初始化并调用入口方法。示例可采用传统 static main(String[]),但不能把 Java SE 26 的所有入口都限定为这个签名。类初始化与实例构造也不同;import 是名称使用规则,不是每次运行到一行就远程加载并执行一个类。Java 启动与初始化

结构 保存什么 生命周期及归属
类元数据、方法及运行时常量池 已加载定义与方法逻辑 JVM/类加载器管理
每线程的 JVM 栈与 frame 局部变量、操作数栈及调用关联 一次方法调用建立 frame,正常或异常离开时结束
堆中的实例 字段与对象关系 可越过调用边界,由 GC 管理可达性
Thread、执行器及 Future 类设施 线程活动、待提交工作和结果 标准库及宿主策略;不要求统一事件循环

JVM 规范区分共享堆与每线程状态,但不规定所有实现的 GC、JIT 或物理内存布局。解释执行、编译后的机器码执行都可实现相同方法语义。JVM 运行时数据区与 frame

开始与等待。 普通方法调用直接进入;Thread.start() 启动线程执行其逻辑,而直接调用 run() 仍是普通方法调用。平台线程、虚拟线程、执行器及异步结果设施各有管理契约。阻塞当前线程不是“Java 必须有一个浏览器式微任务队列”。Thread API

结束与关键词。 return、throw 结束调用,finally 和带资源的 try 处理退出;对象可达性与外部资源关闭分别判断。synchronized 影响互斥和内存可见性,不负责启动应用请求。Spring 中的 bean 和请求生命周期见 Spring Boot 案例。

5. C

入口与执行者。 在 hosted 环境中,启动流程完成必要准备后进入 main;freestanding 环境的启动由实现规定。常见原生实现将函数编译为机器码,由操作系统启动进程、调度线程,CPU 执行指令。C 规范不要求应用事件循环,也不规定一定有某种物理栈布局。C23 公开草案 N3096,5.1.2、6.2.4

结构 保存什么 生命周期及归属
可执行代码、全局及静态对象 函数逻辑和长期状态 加载与启动流程;静态存储期贯穿程序执行
ABI 约定下的寄存器与栈等 参数、局部状态和返回位置 常见实现的一次调用;内联可消除独立栈帧
分配存储区及指针 动态数据与访问地址 malloc/free 等接口管理;C 不提供默认 GC
库自建的回调表、队列或线程记录 外部事件与执行关联 由库或宿主定义,不能从 C 关键词推出

调用与等待。 执行调用表达式进入函数,再返回调用位置。保存函数指针不会自动运行函数。同步 I/O 可能阻塞线程;非阻塞 I/O 与事件循环需要操作系统和库的接口。while 可以写调度循环,也可以只是计算循环,两者用途不同。

结束与关键词。 自动存储期、静态存储期和分配存储期不同;离开函数不自动释放所有动态内存。static、_Thread_local 等影响存储属性,return 结束函数;free、atexit 是库名称。正常退出和强制终止的清理保证不同。完整入口例子见普通进程路径。

6. C++

入口与执行者。 普通原生程序经启动及初始化进入 main,函数通过调用表达式执行。与 C 相比,C++ 特别区分存储的取得、对象生命周期开始、析构和存储释放;这些步骤不能只合称“对象在内存里”。启动、对象生命周期

结构 保存什么 生命周期及归属
编译后的函数与类型相关信息 调用逻辑、布局及所需分派信息 编译实现管理,不要求所有类型都有运行时对象
普通调用状态与自动对象 参数、局部数据和退出时析构关系 正常离开及适用的异常展开触发析构
所有权容器与动态对象 指针及释放责任 如 RAII 包装器;原始指针本身不自动表达所有权
coroutine state、promise 与 awaiter 参数、跨暂停局部状态及继续协议 编译器与库共同管理,最终需要适当销毁协程状态

进入与等待。 co_await、co_yield、co_return 参与协程协议。首次是否暂停由 promise 的 initial_suspend 等决定;等待是否真暂停由 awaiter 决定。C++ 不因为出现 co_await 就自带统一 scheduler 或创建线程。这里的 promise 是协程协议对象,不能直接等同于 JavaScript Promise。协程定义

结束与关键词。 RAII 把资源退出关联到对象析构,try/throw 的异常展开可触发清理;异常终止等情形不能套用普通展开保证。协程逻辑完成后还可能停在最终暂停点,完成和 frame 释放不同;裸 handle 的遗失不自动等于安全销毁。delete、析构及所有权包装器负责相应存续,constexpr 等编译期能力不属于宿主事件调度。析构函数

典型协程轨迹:调用建立状态 → 按 initial_suspend 协议继续或暂停 → awaiter 检查与继续 → 逻辑完成 → 最终暂停及状态销毁按各自协议处理。scheduler 是库或宿主角色,不由协程关键词单独决定。

7. C#

入口与执行者。 以常见 .NET 应用为基线,编译产生程序集中的 CIL 和元数据,运行时通过 JIT 等路径执行;AOT 是另一种路径。入口可由 Main 或顶层语句等构成。CLR 管理执行与内存,不自动为所有应用规定 Unity 的帧循环。托管执行过程

结构 保存什么 生命周期及归属
程序集、类型和方法元数据 可执行定义及类型关系 加载及运行环境管理
普通方法调用状态 参数、局部状态和返回关系 调用进入至返回或异常离开
编译器生成的 async 状态机 跨暂停局部状态、当前位置和 awaiter 一次异步调用;具体表示可优化
Task 与继续处理 完成状态、结果、异常等 可被多方等待;不等于一个专属线程
SynchronizationContext/TaskScheduler 等 继续工作的执行策略 库、宿主和调用配置共同决定

开始与等待。 普通 async Task 方法被调用时会先同步推进;遇到尚未完成的 awaitable 才需要暂停。awaitable 已完成时可以继续同步推进,不能照抄 JavaScript await 的时间模型。继续位置保存在状态机中,恢复线程由捕获的上下文等规则影响,不保证总在原线程。Task 异步模型

结束与关键词。 return 或异常使 Task 得到相应结果;取消是合作处理,不是自动回滚。using/await using 关联资源释放,lock 关联互斥,GC 管理内存可达性。Task 完成、资源 Dispose、对象回收是不同事件。GC 基础

Unity 是另一种宿主组合,具有 Mono/IL2CPP 后端及 PlayerLoop;其 IEnumerator 协程由引擎推进,不等于 C# async Task。Unity 案例

8. Go

入口与执行者。 包变量初始化、init 函数及 main.main 有规定的启动关系。普通函数调用由当前 goroutine 执行;go f(args) 先在调用方求值函数及参数,再让新 goroutine 执行调用。Go 自带 runtime 管理 goroutine,不需要每个应用先自己编写事件循环。Go 程序初始化与执行

结构 保存什么 生命周期及归属
包、编译代码与初始化状态 定义、全局数据及启动顺序 程序初始化和运行
goroutine 的栈及 g 记录 调用链、保存的执行状态及调度状态 runtime 管理一次 goroutine 活动
M 与 P OS 线程、执行 Go 代码所需的运行时资源 当前 runtime 的 G/M/P 实现模型
channel 与等待关系 缓冲、通信状态和发送/接收等待者 程序引用与 runtime 共同维持
堆对象 跨调用、跨 goroutine 的数据 GC 跟踪可达性;不保证每个局部值都进堆

G/M/P 是具体 Go runtime 的实现说明;P 是调度资源,和用户权限没有关系。goroutine 不要求绑定一个永远专属的 OS 线程。Go runtime HACKING、Go GC 指南

等待、结束与关键词。 channel 操作、select 等可让 goroutine 等待,runtime 保存状态并在条件合适时安排继续。defer 在外围函数退出时处理延后工作,不在每次等待时触发。main 返回会结束程序,不等待所有其他 goroutine 自行完成;应用需要明确等待和关闭安排。关闭 channel 表达通信协议,不等于强制杀死全部相关 goroutine。

典型轨迹:包初始化 → main → go 启动活动 → runtime 安排推进 → channel 条件不满足时等待 → 条件满足后继续 → 函数退出并执行 defer。main 是否仍在等待其他工作,由应用的同步逻辑决定。

9. Rust

入口与执行者。 普通原生程序从入口进入函数调用。所有权、借用与生命周期注解约束值的使用;生命周期注解通常是编译期关系,不是运行时事件回调。默认内存管理依赖所有权及 Drop,不要求跟踪式 GC。所有权

结构 保存什么 生命周期及归属
编译后的函数与类型表示 调用逻辑和数据布局 编译器及链接环境管理
普通调用状态与所有权值 参数、局部值及退出释放责任 作用域及移动后的所有者决定存续
async 生成的 Future 状态 跨等待局部数据和进度 Future 的所有者持有;由 poll 推进
Context/Waker 通知 executor 某项工作应再获推进机会 Future 与 executor 的恢复协议
executor 的 task 与队列 Future、调度状态及唤醒关联 所选异步运行库定义;标准语言不指定唯一 executor

进入与等待。 调用 async fn 创建 Future,函数体在 Future 被推进时执行。.await 不自动创建独立并发任务;spawn 由运行库提供。poll 返回 Pending 或 Ready;Waker 通知需要再安排推进,不保证被唤醒就已经有最终结果,也不要求无限忙等。某些 Future 是已启动活动的结果句柄,不能从“Future 惰性推进”推断背后所有工作都尚未开始。async 函数、Future 协议

结束与关键词。 正常作用域退出释放相应拥有的值;panic 的 unwind 与 abort 对析构的保证不同。丢弃 Future 通常停止进一步推进并释放它拥有的状态,但不能撤销已发出的远端操作。move 改变捕获方式,async/await 参与状态保存与推进,loop 本身不构成完整 executor。

典型轨迹:创建 Future → poll → Pending 时保存等待关系 → Waker 通知再调度 → 再次 poll 推进保存的状态 → Ready 交付结果。Future 结束后的内存与资源释放,还取决于所有者。

10. Swift

入口与执行者。 普通调用与应用入口由 Swift 编译代码及宿主启动;UI 应用还要服从应用框架。调用 async 函数不等于必然创建新的独立 Task。await 标记潜在暂停点,不保证每次遇到它都实际暂停。Swift 官方语言书:并发

结构 保存什么 生命周期及归属
编译代码与类型信息 函数逻辑和类型关系 编译工具及运行环境管理
普通调用及 async 继续状态 参数、局部状态和暂停后进度 当前调用与任务活动期间
Task、任务组和取消状态 并发活动、结果及父子管理关系 结构化并发作用域或非结构化任务安排
actor/executor 管理 隔离及工作执行约束 并发运行时;actor 不等于专属线程
ARC 管理的类实例 强、弱、无主引用及实例数据 引用关系决定存续;需要处理强引用环

调度与恢复。 async let、任务组和 Task 等表达不同任务组织方式。潜在暂停允许其他工作推进,恢复后不能一般性假定 OS 线程不变。actor 保证相应隔离边界,跨 await 仍要考虑其他工作可能改变状态。executor 接收并安排可运行的 job,任务可以跨多个这样的执行片段存续。Swift SE-0304 的 task、job 与 executor 模型

结束与关键词。 结构化子任务受外围作用域约束;取消是合作机制。defer 关联作用域退出,deinit 关联类实例销毁,weak/unowned 影响引用关系。ARC 主要解释类实例的内存管理,不应套用为所有值都对应独立引用计数堆对象。Swift 官方语言书:ARC

典型轨迹:建立任务作用域 → 调用 async 函数 → 潜在暂停点实际需要等待 → 保存继续状态 → 获得 executor 的继续机会 → 返回结果及完成作用域清理。任务存续跨越暂停,不是每次恢复都创建一份全新业务计算。

11. Kotlin

入口与执行者。 本节以 Kotlin/JVM 为基线:编译结果由 JVM 执行,普通函数有 JVM 调用状态。Kotlin/JS 和 Kotlin/Native 使用不同目标环境,不能把 JVM 栈和 GC 推广到所有 Kotlin 程序。suspend 表明函数可以参与可暂停调用,不自行创建一个新协程。Kotlin 协程规范

结构 保存什么 生命周期及归属
JVM 方法与类元数据 编译后的逻辑及定义 JVM 目标环境管理
Continuation 与状态机 结果接收位置、跨暂停局部状态和进度标签 可暂停计算的继续关系;不是当前 OS 线程的别名
CoroutineContext dispatcher、Job 等上下文元素 协程库及继承、覆盖规则管理
Job/Deferred 活动状态、父子关系、取消及结果 kotlinx.coroutines 的管理对象,不是语言关键词
dispatcher 与等待管理 决定工作在哪种执行环境推进 所选库和宿主提供

进入与等待。 launch、async 等 builder 由 kotlinx.coroutines 创建并组织活动;开始时机受启动模式影响。调用 suspend 函数可继续在现有协程中执行,不等于 spawn。编译器可将暂停路径转换为带局部字段及状态标签的继续结构;恢复可能更换线程,也可能依据 dispatcher 保持特定线程约束。协程上下文与 dispatcher

结束与关键词。 Job 完成、子任务结束、取消传播及 finally 清理分别遵循协程库与语言规则。JVM 的 GC 管理内存,业务资源仍需合适的关闭协议。fun、suspend、try/finally 分别涉及定义、暂停能力和退出;Job、launch、async 在这里是库设施。

典型轨迹:builder 组织活动及上下文 → 按启动模式进入 → suspend 调用实际暂停时保存状态 → 条件满足后通过 Continuation 恢复 → 返回或异常 → Job 处理完成及子活动关系。继续逻辑和 dispatcher 的执行位置规则共同决定下一步。

12. Julia

入口与执行者。 Julia REPL、脚本及 include 可以执行顶层代码。解析、宏展开和 lowering 后,由运行时选择解释或编译等路径处理;函数定义增加方法,不意味着当场执行函数体。调用根据方法分派选择逻辑,运行时可编译相应专门化。Julia 执行路径

结构 保存什么 生命周期及归属
Module、方法及专门化相关表示 绑定、方法逻辑和可执行代码 Julia 运行时管理
普通调用状态 参数、局部状态和返回关系 一次调用
Task 可暂停执行状态、结果及活动关系 由程序创建、运行时调度
Channel 和等待者 通信内容及等待关联 任务与 Channel 协议管理
运行时值表示 值、类型及必要的对象头等 运行时管理;优化后的值不保证均是单独装箱对象

Julia 的 jl_value_t 等是实现层的对象表示;它回答值怎样存储,不决定哪个函数什么时候被调度。实现文档的历史细节不能直接充当完整稳定 ABI。Julia 对象布局

进入与等待。 创建 Task 不立即执行其函数体;需要调度或其他推进安排。@async、Threads.@spawn 等宏会创建并安排任务,但执行位置约束不同。wait、Channel 等可以让当前任务等待,运行时安排其他工作;暂停后保留状态,不从函数开头重新运行。Julia 异步编程

结束与关键词。 return 或异常结束本次调用,try/finally 关联退出清理。GC 与外部资源关闭分开处理;do 可把函数传给资源管理 API,具体关闭保证取决于被调用的 API。Task、schedule、wait、Channel 及这些宏不因为参与生命周期就都变成关键词。

典型轨迹:创建 Task → 调度 → 执行函数体 → wait/通信形成等待 → 条件满足后继续 → 返回或失败,等待者取得相应结果。Task 对象及结果可以在活动结束后继续被引用。

横向比较:什么动作真正进入执行

下表只比较所述常见用法。对象创建、提交调度和获得执行机会之间,不能用同一个“启动”掩盖差别。

用法 这一动作的直接效果 下一步由谁推进
各语言的普通同步函数调用 当前执行路径进入函数体 当前线程/执行引擎继续
Python async def 调用 创建 coroutine,未执行函数体 await、Task 等驱动
JavaScript async 调用 执行同步前缀,返回 Promise 引擎及宿主协作推进异步继续
C# 常规 async Task 调用 同步推进,必要时在未完成 awaitable 处暂停 状态机、awaiter 与上下文策略
Rust async fn 调用 创建 Future await 或 executor 的 poll
C++ 协程调用 建立协程协议及状态,首次暂停规则由 promise 决定 调用者、awaiter 或库的 scheduler
Go go 语句 求值调用信息并启动新的 goroutine Go runtime 调度
Swift async 调用 在当前任务中调用可暂停函数;不必创建新 Task Swift 并发执行机制
Kotlin suspend 调用 在可暂停调用链中执行;不必创建新 Job Continuation 与协程上下文
Julia Task 构造 创建任务,未运行函数体 调度及运行时推进
React 状态更新 请求一次界面更新,不等于直接执行每个 Effect React 组织渲染与提交,宿主提供执行机会

生命周期设计的比较结论

以下是基于上面机制的分析归纳,不是任何语言的统一规范。

  • 循环不是所有生命周期的必要形式。 C++ 自动对象可以随作用域构造和析构;普通调用可以直接进入并返回。循环适合持续接受工作,但不定义全部存续关系。
  • 钩子不是全部调度机制。 钩子连接条件与逻辑;优先级、执行位置、等待及取消还需要独立规则。命名回调、函数参数、订阅和协程继续位置是不同的连接形式。
  • Task 不等于整个对象体系。 数据值、代码定义、一次执行、结果句柄和长期组件应分别说明。可以共享基础对象模型,但不能因此共享同一执行状态机。
  • 宏任务/微任务是特定宿主的顺序契约。 是否采用此类分层,应由可观察顺序、更新边界及响应性需求决定,不能从“需要循环”直接推出。
  • 长期保存定义不等于长期执行。 会话、模块、平台对象和订阅都可以存续而暂时没有用户代码运行。

Endorphin 已有的阶段定义见 Preflight 与 inflight,普通调用见函数与调用,会话提案见 REPL。需要进一步明确的执行契约集中在框架比较后的设计分析。

关键词手册 · 框架如何接管代码执行 · 返回目录