第 11 讲:CodexThread 与 ThreadManager

Codex 源码解析 · 第 11 讲

Codex 源码解析系列 第 11 讲:CodexThread 与 ThreadManager 基于 OpenAI Codex 源码 · 2026-10-05 💡 本讲一句话: 第 10 讲的 run_turn 只是"一个 turn 怎么跑",这一讲回答"线程本身怎么活下来": CodexThread 是 Session 的门面(facade),ThreadManager 是线程的户籍科 ——创建、去重、注册、隔离、移除全在它手里。读完你会明白 Codex 为什么敢让多个客户端、子代理、Guardian 审查器同时挂在同一个进程里而不打架。 一、先建立地图:thread = Session 的门面 + 户籍表 第 8-10 讲我们一直在 Session 内部打转:TurnContext、系统提示词、agent loop。但外部世界(TUI、app-server、子代理)从来不直接碰 Session——它们手里拿的是一个叫 CodexThread 的东西,由一个叫 ThreadManager 的组件统一发放和管理。 源码里这条链路横跨两个文件: core/src/codex_thread.rs (1012 行,门面本体)和 core/src/thread_manager.rs (2492 行,户籍科)。本讲按"结构定义 → 双向通道 → turn 提交三模式 → 挂起交接 → 共享状态 → spawn 主流程 → 注册握手 → 生命周期管理"的顺序拆完这两个文件的核心路径。 📌 一句话定位: CodexThread ≈ "会话的遥控器"(只转发、不干活);ThreadManager ≈ "户籍科 + 资源池"(决定谁能出生、谁可见、怎么注销)。真正的执行体 Session 藏在两者背后。 二、CodexThread:一个只有 7 个字段的门面 先看结构体本身。它小到让人意外——没有配置、没有历史、没有任何业务状态,全部委托给 Arc<Session> : 📄 codex-rs/core/src/codex_thread.rs (第 179-224 行) pub struct CodexThread { // 线程门面:外部调用方(TUI/app-server/子代理)看到的只有它,看不到 Session pub(crate) session: Arc<Session>, // 真正的运行时在 Session;thread 只持 Arc 引用、不拥有其生命周期 pub(crate) io: SessionIo, // 双向通道端点:提交 op / 接收事件都走这里(下一节细拆) pub(crate) session_source: SessionSource, // 注册来源决定访问权与生命周期钩子(用户/内部/Guardian) session_configured: SessionConfiguredEvent, // 启动时首个事件的快照,供外部查询线程配置 rollout_path: Option<PathBuf>, // rollout 录制文件路径;ephemeral 或未落盘时为 None out_of_band_elicitations: Mutex<OutOfBandElicitations>, // 带外 elicitation 状态(计数+注册),用互斥锁保护 _diagnostics_guard: GaugeGuard, // drop guard:线程被丢弃时自动把 LIVE_THREADS 指标减一 } impl CodexThread { // 门面方法组:所有对外入口都挂在这里 pub(crate) fn new( // 内部构造器:只有 ThreadManager 能创建线程(注册是它的职责) session: Arc<Session>, // 运行时本体,与 SessionIo 等持有者共享同一份 Arc io: SessionIo, // 通道端点;所有发送方 drop 掉即终止 session loop session_configured: SessionConfiguredEvent, // 首事件必须是它,否则 finalize_thread_spawn 会报错 rollout_path: O…

阅读全文:Codex 源码解析 第 11 讲