sharedarraybuffer 必须通过 postmessage 的 transfer list 显式传递,worker 中从 ports[0] 获取;atomics.wait 仅限 worker 调用;整数视图需全局统一;cas 操作必须用 atomics.compareexchange。

SharedArrayBuffer 必须显式传递,不能靠闭包或全局变量“共享”
主线程创建的 SharedArrayBuffer 实例,在 Worker 内不会自动可见。常见错误是:主线程里定义 const sab = new SharedArrayBuffer(1024),然后在 Worker 里直接写 new Int32Array(sab) —— 这会报 ReferenceError: sab is not defined 或更隐蔽的 TypeError: invalid argument to Int32Array。
正确做法只有且必须是:postMessage() 的第二个参数传入 transfer list:
const sab = new SharedArrayBuffer(1024);
const ia = new Int32Array(sab);
worker.postMessage({ cmd: 'init' }, [sab]); // ← 关键:[sab] 表示移交所有权
Worker 中接收时,不能从 event.data 里取 sab,而要从 event.ports[0] 拿:
self.onmessage = ({ ports }) => {
const sab = ports[0]; // ← 不是 event.data.buffer
const ia = new Int32Array(sab);
};
- 漏传
[sab]→ Worker 收到的是普通 ArrayBuffer 副本,Atomics操作全部失效 - 误用
event.data.sab→undefined,视图构造失败,后续所有Atomics调用静默失败或抛错 - 多个 Worker 共享同一
sab时,每个都必须单独postMessage(sab, [sab]),不可复用 port
Atomics.wait() 只能在 Worker 里调用,主线程调用直接报错
Atomics.wait() 和 Atomics.waitAsync() 是唯一能“阻塞等待内存变化”的原生机制,但它们被浏览器明确禁止在主线程执行。如果你在主线程写:
Atomics.wait(int32View, 0, 0); // ← TypeError: Atomics.wait is not supported on this thread
就会立刻抛出错误。这个限制不是 bug,而是设计使然:主线程阻塞 = UI 卡死,浏览器绝不允许。
可行替代方案只有两种:
- Worker 内用
Atomics.wait()等待信号,再通过postMessage()主动通知主线程 - 主线程改用轮询 +
Atomics.load()(注意性能损耗),例如:while (Atomics.load(ia, 0) === 0) {}—— 仅限极短等待,否则吃满 CPU -
Atomics.waitAsync()返回 Promise,适合 Worker 内异步逻辑编排,但不能解决主线程等待问题
整数视图类型必须严格一致,混用 Uint8Array 和 Int32Array 会踩内存越界
同一个 SharedArrayBuffer 上,不同 Worker 如果用不同视图类型读写同一块地址,结果完全不可预测。典型错误:
- 主线程用
new Int32Array(sab)写第 0 个 32 位整数(占字节 0–3) - Worker A 用
new Uint8Array(sab)读字节 0,得到低 8 位值 - Worker B 同时用
new Int32Array(sab)对索引 0 执行Atomics.add()
这时 Uint8Array[0] 和 Int32Array[0] 指向同一内存起始地址,但解释方式冲突,B 的原子加法可能把 A 正在读的字节覆盖掉,或反之。最终数据既不是你写的 int,也不是你读的 byte。
安全做法只有一条:所有线程对同一 sab 使用完全相同的构造方式生成视图,例如统一约定:
// 全局协议:前 4 字节为状态码(Int32),后 1020 字节为 payload(Uint8Array) const sab = new SharedArrayBuffer(1024); // 所有线程都这样切分: const status = new Int32Array(sab, 0, 1); // offset=0, length=1 const payload = new Uint8Array(sab, 4, 1020); // offset=4
Atomics.compareExchange 是唯一能安全实现“检查-设置”逻辑的原语
想实现“如果 flag 是 0,就设成 1,并返回成功”,绝不能拆成两步:
// ❌ 危险!中间存在竞态窗口
if (Atomics.load(ia, 0) === 0) {
Atomics.store(ia, 0, 1); // 其他线程可能在这行之前/之后修改了 ia[0]
}
必须用 Atomics.compareExchange() 一次性完成读-判-写:
const expected = 0; const desired = 1; const result = Atomics.compareExchange(ia, 0, expected, desired); // result 是旧值;若 result === expected,则设置成功
-
Atomics.compareExchange()是硬件级 CAS 指令的封装,不可中断 - 它只作用于单个整数位置,无法跨字段(比如同时检查 status 和 version 两个 int)
- 若需多字段协调,只能靠预留额外标志位、状态机编码,或退回到消息+锁机制
真正难的不是写对语法,而是把业务逻辑映射成几个整数位上的原子操作 —— 这一步没抽象好,后面所有 Atomics 都白搭。











