最新下载
热门教程
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
如何用 SharedArrayBuffer 与 Atomics.load 实现高效多 Worker 轮询
时间:2026-06-13 09:45:07 编辑:袖梨 来源:一聚教程网
Atomics.load 不适合轮询,应配合 Atomics.wait 使用;轮询会耗尽 CPU、阻塞线程且低效,而 wait/notify 零开销、毫秒响应,并需确保 crossOriginIsolated 环境。
Atomics.load 本身不适合轮询,强行用会吃光 CPU、卡死线程,且根本达不到“高效”——它只该用在配合 Atomics.wait 的同步链路里。
为什么不能用 Atomics.load 做轮询
轮询(polling)指 Worker 不停地调用 Atomics.load(view, index) 检查某个标志位是否变化。这看似简单,但问题严重:
- 每次
Atomics.load都是内存访问,无等待、无让出,纯空转 —— 在 4 核机器上可能把一个 Worker 线程跑满 100% CPU - 浏览器无法调度休眠,线程持续抢占时间片,影响其他 Worker 或主线程响应
- 即使你加
setTimeout或await new Promise(r => setTimeout(r, 1)),也变成低效假轮询,延迟不可控、唤醒不及时 -
Atomics.load不触发任何通知机制,无法与生产者形成协作节奏,纯属单向瞎猜
真正高效的替代方案:Atomics.wait + notify
高效跨 Worker 协作不是“查”,而是“等”。Atomics.wait 让线程挂起,直到被明确唤醒,零 CPU 开销,毫秒级响应。
- Worker 启动后,先调用
Atomics.wait(sharedView, 0, 0)—— 表示“等索引 0 变成非 0” - 主线程或上游 Worker 完成数据写入后,执行
Atomics.store(sharedView, 0, 1)再紧跟着Atomics.notify(sharedView, 0, 1) - 被唤醒的 Worker 拿到控制权,处理数据,完成后可重置标志(如
Atomics.store(sharedView, 0, 0)),再回到wait - 注意:
Atomics.wait只能在 Worker 中安全调用;主线程调用会直接抛TypeError
共享内存布局建议(避免踩坑)
别把所有状态塞进一个整数。多 Worker 协作时,内存布局混乱是竞态高发区:
- 前 4 字节(
Int32Array[0])专用于同步标志(0=空闲,1=就绪,2=忙) - 接下来 4 字节(
Int32Array[1])存数据长度,防止越界读取 - 剩余空间按需划分数据区,例如
new Float64Array(sab, 8)存计算结果 - 所有写入必须用
Atomics.store,所有读取必须用Atomics.load—— 即使只是读长度,也不能直接view[1] - 若需多个 Worker 并行写不同区域,确保它们操作的索引完全不重叠,否则仍需
Atomics.compareExchange加锁
检查环境是否就绪再动手
代码写得再对,环境不达标就全白搭。运行前务必确认:
- 服务端已返回两个关键 header:
Cross-Origin-Opener-Policy: same-origin和Cross-Origin-Embedder-Policy: require-corp - 页面中所有脚本、Worker 文件加载都带
crossorigin属性,例如<script src="worker.js" type="module" crossorigin></script> - 开发时不要双击打开 HTML,必须走
http://localhost:8080类地址;file://协议下self.crossOriginIsolated永远为false - 在 Worker 或主线程中打印
self.crossOriginIsolated,为true才能继续;否则new SharedArrayBuffer()会静默失败或报SharedArrayBuffer is not defined
最容易被忽略的是:你以为自己在“轮询”,其实只是没意识到 Atomics.wait 才是唯一合理入口;而所有看似省事的“手动 sleep + load”组合,都在悄悄拖垮性能和可维护性。