SharedArrayBuffer 默认被禁用是因为防范 Spectre 等侧信道攻击,必须满足跨源隔离:主页面响应头需同时设置 Cross-Origin-Opener-Policy: same-origin 和 Cross-Origin-Embedder-Policy: require-corp,Worker 脚本须合规加载,且传递时必须通过 postMessage(..., [sab]) 显式转移,否则无效;未配合 Atomics 同步的并发读写必然导致数据损坏。

SharedArrayBuffer 是唯一能真正实现多 Worker 零拷贝共享原始内存的机制,但默认被禁用,且必须配合 Atomics 使用,否则读写竞争必然导致数据损坏。
为什么直接 new SharedArrayBuffer 会失败
现代浏览器(Chrome 92+、Firefox 94+)默认禁用 SharedArrayBuffer,以防 Spectre 类漏洞。页面需满足跨域隔离策略才能启用——不是加个 header 就行,而是整个上下文必须明确声明安全边界。
- 主页面响应头必须同时包含:
Cross-Origin-Embedder-Policy: require-corp和Cross-Origin-Opener-Policy: same-origin - Worker 脚本必须通过
importScripts()或new Worker(..., { type: 'module' })加载,不能用内联 blob URL(除非该 blob 显式标记为COEP: require-corp) - 若用
fetch()加载 Worker 脚本,请求也需带credentials: 'same-origin',否则响应不被视为“可嵌入”
如何安全传递 SharedArrayBuffer 给多个 Worker
postMessage() 是唯一合法途径,且必须显式将 SharedArrayBuffer 放入 transferList 参数,否则会被结构化克隆(即复制,失去共享意义)。
- 主页面创建:
const sab = new SharedArrayBuffer(1024 * 1024 * 100); // 100MB - 发送给 Worker A:
workerA.postMessage({ buffer: sab }, [sab]); - 发送给 Worker B:
workerB.postMessage({ buffer: sab }, [sab]); - ⚠️ 错误写法:
worker.postMessage({ buffer: sab })—— 这里没传transferList,sab会被忽略或报错
为什么不用 Atomics 就一定会出错
SharedArrayBuffer 的内存区域不提供任何同步保障。多个 Worker 同时写同一 Int32Array 索引,结果不可预测——不是“最后写入胜出”,而是可能产生撕裂值(torn write),比如 32 位写入只完成高 16 位。
- 基础同步用
Atomics.wait()/Atomics.notify()实现条件等待 - 计数器增减必须用
Atomics.add(view, index, delta),不能用view[index]++ - 标志位设置推荐
Atomics.or(view, index, 1)+Atomics.and(view, index, ~1),避免读-改-写竞态 - 简单轮询检查某位置是否就绪?用
Atomics.load(view, index),别用view[index](可能读到陈旧值)
常见性能陷阱与替代建议
共享内存本身不慢,但滥用 Atomics 会严重拖慢——每次调用都隐含内存屏障和 CPU 缓存同步开销。高频更新单个位置(如帧计数器)没问题;高频随机写入大数组不同位置,反而比消息通信更慢。
- 避免在 tight loop 中反复调用
Atomics.load(),改用局部缓存 + 周期性Atomics.load()检查变更 - 若数据是只读分发(如纹理、音频样本),考虑用
Transferable+ArrayBuffer一次性传输,比长期持SharedArrayBuffer更轻量 - 需要复杂结构(对象、字符串)?别硬塞进
SharedArrayBuffer——用MessageChannel配合结构化克隆更稳妥,SharedArrayBuffer只适合Int8Array/Float32Array这类原始数值批量操作
真正难的不是创建或传递,而是设计好访问协议:谁写、谁读、何时同步、冲突如何退避——这些逻辑一旦出错,bug 只在高负载、多核机器上偶现,极难复现和调试。











