Atomics.wait配合SharedArrayBuffer可在Worker间构建协作式阻塞同步原语,但仅限Worker内使用,需跨域隔离、Int32Array视图、严格wait/notify配对及compareExchange抢锁,主线程调用直接报错。

Atomics.wait 配合 SharedArrayBuffer 能在 Worker 间构建真正阻塞、低开销的同步原语,但它不是“线程睡眠”,也不是主线程可用的工具——它是一套协作式等待机制,必须严格满足运行环境与内存访问规范才能生效。
前提条件:跨域隔离与共享内存初始化
浏览器强制要求跨域隔离上下文才能启用 SharedArrayBuffer:
- 服务端必须返回两个响应头:
Cross-Origin-Embedder-Policy: require-corp和Cross-Origin-Opener-Policy: same-origin - 本地开发不能用
file://协议,需通过localhost启服务(例如npx serve -l 8080) - 主线程创建
SharedArrayBuffer并用Int32Array视图初始化锁变量(如首元素设为 0),再通过postMessage(sab, [sab])传给各 Worker —— 注意是转移底层缓冲区,不是拷贝 - 所有 Worker 和主线程必须使用完全一致的视图:同为
new Int32Array(sab),且访问同一索引(如[0])
Worker 中实现可重入的等待-加锁循环
每个 Worker 在进入临界区前,需主动等待锁空闲并原子抢锁,不能假设一次 wait 就能成功:
- 调用
Atomics.wait(view, 0, 0, 1e5):在索引 0 等待值变为非 0,超时设为 100 秒防永久挂起 - 返回
"ok"不代表已加锁,只是被唤醒;必须再次检查view[0] === 0(防止虚假唤醒或信号中断) - 确认空闲后,用
Atomics.compareExchange(view, 0, 0, 1)原子地将 0 → 1;若返回值不是 0,说明已被其他 Worker 抢占,需重新循环 - 执行完临界区逻辑后,先
Atomics.store(view, 0, 0)清锁,再立刻Atomics.notify(view, 0, 1)唤醒一个等待者
避免常见误用导致的失效或死锁
这类同步原语非常脆弱,细节错一点就退化为轮询或永远卡住:
-
Atomics.wait只能在 Worker 内调用,主线程调用直接抛TypeError - 视图类型必须是
Int32Array(或BigInt64Array),用Float64Array或Uint8Array会报RangeError - 等待时传入的期望值(第三个参数)若与当前值不等,函数立即返回
"not-equal",不会挂起 —— 所以必须确保初始状态和唤醒逻辑对齐 - 不要用
Atomics.add(view, 0, 1)模拟锁,它无法阻塞,也不提供唤醒能力;wait/notify是为“有等待需求”的场景设计的
适用边界:什么情况该用,什么情况不该用
它适合需要显式协调执行顺序的场景,而非所有并发操作:
- 适合:多 Worker 分批处理任务队列、屏障同步(所有 Worker 到达某点才继续)、生产者-消费者模型中的资源就绪通知
- 不适合:单纯计数(用
Atomics.add更轻量)、主线程参与同步(主线程不支持 wait)、频繁短临界区(唤醒开销可能反超轮询) - 注意:Node.js 需启动时加
--experimental-shared-array-buffer,且默认禁用 SAB,WASM 多线程还需 Emscripten 开启-pthread和USE_PTHREADS=1


















