SharedArrayBuffer + Atomics 可构建跨线程状态机,但须满足跨域隔离(COOP/COEP 头)、使用 Int32Array 单索引+Atomics.compareExchange 控制状态跃迁,禁止普通读写与多字段共用同一原子位。

SharedArrayBuffer + Atomics 能构建跨线程状态机,但必须用原子操作控制状态跃迁,否则状态不一致是必然结果。
SharedArrayBuffer 初始化必须满足跨域隔离
浏览器会直接禁用 SharedArrayBuffer,除非页面运行在跨域隔离上下文中。这不是可选优化,而是硬性前提。
- 服务端必须返回两个 HTTP 头:
Cross-Origin-Opener-Policy: same-origin和Cross-Origin-Embedder-Policy: require-corp - 缺失任一头部,
new SharedArrayBuffer(1024)会抛出TypeError: SharedArrayBuffer is not supported in your environment - 本地开发时不能靠
file://协议测试,必须走http://localhost或 HTTPS
状态机状态必须映射到 Int32Array 的单个索引
用 Atomics 控制状态迁移,核心是把状态值存进共享视图的固定位置,并用原子操作读写——不能用普通赋值或读取。
- 定义状态常量:例如
const STATE_IDLE = 0, STATE_RUNNING = 1, STATE_DONE = 2 - 创建视图:
const stateView = new Int32Array(sharedBuffer, 0, 1)(只占 1 个 32 位整数) - 设置初始状态:
Atomics.store(stateView, 0, STATE_IDLE),不是stateView[0] = STATE_IDLE - 读取当前状态:
Atomics.load(stateView, 0),不是stateView[0]
状态跃迁必须用 Atomics.compareExchange
仅靠 load + store 无法防止竞态:两个线程同时读到 STATE_IDLE,又都写入 STATE_RUNNING,就丢失了一次意图。
- 安全跃迁示例(从 IDLE → RUNNING):
let expected = STATE_IDLE; let desired = STATE_RUNNING; let result = Atomics.compareExchange(stateView, 0, expected, desired); if (result === STATE_IDLE) { // 成功抢占,进入临界区 } else { // 失败:别人已抢先修改,需重试或放弃 } - 不要用
Atomics.add或Atomics.or模拟状态机——它们不表达“条件性状态变更”语义 - 若需等待状态变化(如主线程等 worker 进入 DONE),用
Atomics.wait(stateView, 0, STATE_RUNNING),但注意它会挂起线程,且需配对Atomics.notify
Worker 间通信应避免重复序列化大块数据
状态机本身只管控制流;数据流应复用同一块 SharedArrayBuffer 的其他区域,而非通过 postMessage 传副本。
- 把数据视图和状态视图放在同一 buffer 不同 offset:
new Int32Array(sharedBuffer, 4, 1000)存计算输入,new Int32Array(sharedBuffer, 0, 1)存状态 - Worker 修改数据后,只需用
Atomics.store(stateView, 0, STATE_DONE)通知,主线程无需接收完整数组 - 切忌在
postMessage中传sharedBuffer—— 它本身不可结构化克隆,传过去仍是引用,但没意义;只需传必要元信息(如“第3号任务完成”)
最容易被忽略的是:状态机逻辑一旦涉及多个字段(比如状态 + 错误码 + 进度),就不能只靠一个 Int32Array 索引维护——必须拆成多个原子字段,或升级为自定义原子结构(如用 Atomics.compareExchange 操作带掩码的复合整数),否则任意字段更新都会破坏其他字段一致性。


















