并发与运行时底层
L20 ★ 结构化并发与异步中断
本课目标
- 区分四种 fork 的生命周期绑定
- 解释中断如何跨异步边界传播且保证清理
前置课时尚未完成:L16、L17
可以自行跳转学习,但按顺序推进会更省力;学情分析会把这类缺口归入「前置缺口」。
子 fiber 的寿命由谁决定
Effect 采用结构化并发:子 fiber 的生命周期默认绑定父 fiber。四种 fork 策略改变的是「绑定到谁」:forkChild 绑父(自动监督)、forkDetach 绑全局(daemon)、forkScoped 绑局部 scope、forkIn 绑指定 scope。
四种 fork 与中断
← 代码可左右滑动 →
import { Effect, Fiber, Console } from "effect"
const child = Effect.repeat(
Console.log("tick"),
Schedule.fixed("1 second")
)
const parent = Effect.gen(function* () {
yield* Effect.forkChild(child) // 父结束 → 子被终止
// Effect.forkDetach(child) // 父结束/被中断,子仍继续
// Effect.forkScoped(child) // 局部 scope 关闭时终止
yield* Effect.sleep("3 seconds")
})
// 中断并等待 finalizer 执行完
const stop = Effect.gen(function* () {
const f = yield* Effect.forkChild(parent)
yield* Effect.sleep("1 second")
yield* Fiber.interrupt(f) // 返回 Effect<void>,等待完全终止
// f.interruptUnsafe() // fire-and-forget,不等 finalizer
})💡 Effect.onInterrupt 注册只在中断时触发的清理;Effect.forEach 并发中任一子任务被中断,其余并发任务一起被中断。
🔬 为什么不用轮询中断
轮询标志位需要程序员记得检查,漏一次就永远不响应,且无法与纯函数式组合兼容。Effect 采用完全异步的信号机制:目标 fiber 不轮询,运行时在安全的指令边界注入中断;临界区通过关闭可中断性来保护。中断请求沿 fiber 树向下传播,Scope 的 finalizer 保证资源收尾。
交互演练
关键术语
结构化并发
子任务生命周期绑定父任务,取消与清理可整体传播
异步中断
运行时在安全边界注入取消信号,无需目标轮询
常见误区
× 用 forkDetach 做「顺手起个后台任务」,结果应用退出时它还挂着。
× 误以为 race 的失败者会自动停止——需要确认它已被中断并清理。
随堂测验(即时反馈,答题计入掌握度)
Q1父 fiber 被中断时,forkDetach 出来的 fiber?
Q2Fiber.interrupt 与 interruptUnsafe 的差别?