# 三行速读

  1. 这章把慢任务从主循环剥离到后台执行槽位。
  2. 后台任务结果通过通知队列回流,主 loop 不被阻塞。
  3. 任务目标(s12)与运行槽位(s13)是两层不同状态。

# 先修知识

  • 已理解 s12 的持久任务图。
  • 熟悉线程 / 异步基础概念(至少知道 “后台执行” 含义)。

# 读完后你应该能做到(可检验清单)

# 本篇要解决什么

s12 解决 “任务目标持久化”,s13 解决 “慢任务执行不阻塞主循环”。很多真实命令需要几十秒甚至几分钟,若同步等待,整个系统吞吐会很差。

核心目标:让慢任务在后台跑,前台 loop 继续推进。

# 用一个类比先理解

像厨房出餐:

  • 慢炖菜放后厨慢慢做;
  • 前台继续接单、处理快菜;
  • 炖好后通过叫号通知取餐。

后台任务 + 通知队列就是这个模式。

# 为什么这一步必须现在做

后续调度、自治队友都会依赖运行时执行槽位。没有后台机制,系统很容易被单个慢操作拖死。

# 关键代码怎么读

# 1) 通知队列

1
2
3
4
class NotificationQueue:
PRIORITIES = {"immediate": 0, "high": 1, "medium": 2, "low": 3}
def push(self, message: str, priority: str = "medium", key: str = None): ...
def drain(self) -> list[str]: ...

有优先级、有折叠(key)就能避免通知噪音泛滥。

# 2) 后台启动即返回

1
2
3
4
5
6
def run(self, command: str) -> str:
task_id = str(uuid.uuid4())[:8]
...
thread = threading.Thread(target=self._execute, args=(task_id, command), daemon=True)
thread.start()
return f"Background task {task_id} started..."

这个 “立即返回” 是吞吐提升的关键。

# 3) 完成后写日志并入通知

1
2
3
4
def _execute(self, task_id: str, command: str):
...
output_path.write_text(final_output)
self._notification_queue.append({"task_id": task_id, "status": status, ...})

详细结果进文件,消息里只放预览,兼顾可追溯和上下文轻量。

# 4) 卡住任务检测

1
2
3
def detect_stalled(self) -> list[str]:
if elapsed > STALL_THRESHOLD_S:
stalled.append(task_id)

这让系统能主动发现 “长时间 running 但无进展” 的异常。

# 你可以怎么复现

  1. 把一个慢命令改为 background_run
  2. 在主 loop 同时做其他任务。
  3. 下一轮 drain 通知,确认结果回流。

# 常见误区

  • 误区 1:把后台任务当持久任务图本身。
  • 误区 2:通知不清理,导致重复注入旧状态。
  • 误区 3:没有输出落盘,失败后难复盘。

# 一句话总结

s13 把 “等待” 从主循环里移出去,让系统真正具备并行推进能力。

# 补充解读:后台槽位与通知回流细节

# A. Runtime 记录文件的意义

1
2
3
RUNTIME_DIR = WORKDIR / ".runtime-tasks"
self._record_path(task_id) -> .json
self._output_path(task_id) -> .log

.json 记录状态, .log 存完整输出。两者分开能兼顾状态查询和结果回放。

# B. _preview() 是上下文保护阀

1
2
3
def _preview(self, output: str, limit: int = 500) -> str:
compact = " ".join((output or "(no output)").split())
return compact[:limit]

它在限制通知体积,防止长日志回灌主上下文。

# C. check() 支持单任务与总览

这对调试很有用:既能看单个后台任务细节,也能看整体队列健康状态。

# D. drain_notifications() 的消费语义

1
2
3
with self._lock:
notifs = list(self._notification_queue)
self._notification_queue.clear()

明确 “一次取走并清空”,防止重复处理。

# 进阶练习

  1. 为后台任务加 cancel 机制。
  2. 给 stalled 任务加自动告警消息。
  3. 测试多后台任务同时完成时的通知排序策略。

# 统一术语口径(本章)

  • Runtime Slot :后台执行中的运行时槽位记录。
  • Notification Queue :后台结果回流主循环的通知队列。
  • Stalled Task :超时未推进的可疑运行任务。

# 章节衔接(从易到难)

  • 本章解决 “慢任务不阻塞”。
  • 下一章 s14 进入 “时间触发如何接入同一执行主线”。