sharedarraybuffer 配合 atomics.add 可实现多线程无锁原子计数器,适用于请求统计等高并发监控场景;需启用跨域隔离,所有写操作必须用 atomics,读推荐 atomics.load,注意溢出、兼容性及索引冲突。

SharedArrayBuffer 配合 Atomics.add 可以在多线程(Web Worker)环境中实现真正无锁、线程安全的原子计数器,适用于高并发场景下的实时监控,比如请求统计、限流计数、并发连接数跟踪等。
核心原理:共享内存 + 原子操作
SharedArrayBuffer 提供一块多个线程可同时访问的原始内存区域;Atomics.add 则保证对这块内存中某个整数位置的加法操作是原子的——不会被其他线程中断或覆盖。这避免了传统锁机制带来的性能开销和死锁风险。
注意:使用 SharedArrayBuffer 需启用跨域隔离(Cross-Origin Isolation),即服务端需返回以下两个响应头:
- Cross-Origin-Embedder-Policy: require-corp
- Cross-Origin-Opener-Policy: same-origin
基础实现:一个可共享的原子计数器类
主页面(主线程)创建 SharedArrayBuffer,并将其传递给 Worker;Worker 通过 Atomics.add 对同一内存地址进行累加。
示例代码结构:
// 主线程
const sab = new SharedArrayBuffer(4); // 4 字节,存一个 Int32Array 元素
const counter = new Int32Array(sab);
counter[0] = 0; // 初始化为 0
// 启动 Worker 并传入共享内存
const worker = new Worker('counter-worker.js');
worker.postMessage({ sab });
// 实时读取当前值(非阻塞,无需 Atomics.load 也可读,但读取可能非最新)
function getCounterValue() {
return counter[0]; // 普通读取(最终一致性)
// 或用 Atomics.load(counter, 0) 确保读到写入完成的值
}
// counter-worker.js
let counter;
self.onmessage = function(e) {
const { sab } = e.data;
counter = new Int32Array(sab);
};
// 模拟高并发增量(例如每收到一个消息就 +1)
self.onmessage = function(e) {
if (e.data.type === 'increment') {
Atomics.add(counter, 0, 1); // 原子地对索引 0 处加 1
}
};
高并发监控的关键实践
单纯累加不够,真实监控还需支持:实时读取、速率计算、阈值告警、归零重置等。
- 避免频繁读取导致性能抖动:不要在循环中高频调用 Atomics.load。可采用“采样+差值”策略,例如每 100ms 读一次,再算每秒增量。
- 支持安全重置:用 Atomics.exchange(counter, 0) 原子替换当前值并返回旧值,适合做滑动窗口统计。
- 防溢出保护:Int32Array 有符号范围是 [-2³¹, 2³¹−1]。若预期计数超 21 亿,改用两个 32 位字段模拟 64 位计数,或定期归零+记录轮次。
- 结合 Performance.now() 打点:在关键路径插入时间戳(同样写入 SharedArrayBuffer 的相邻位置),便于后续分析延迟分布。
常见陷阱与规避方式
SharedArrayBuffer 不是万能银弹,误用反而引发难以调试的问题:
- 未启用跨域隔离 → SharedArrayBuffer 构造失败:检查控制台报错,确认响应头和服务部署配置。
- 主线程直接修改 counter[0](非 Atomics)→ 竞态仍存在:所有写操作必须走 Atomics.xxx,读操作建议也统一用 Atomics.load。
- 多个 Worker 写不同索引但共享同一 buffer → 必须确保索引不重叠:例如按 Worker ID 分配 slot,或用单一计数器+合理业务建模。
- 浏览器兼容性:Chrome 68+、Firefox 78+、Edge 79+ 支持;Safari 目前仍限制使用(仅部分 iOS 16.4+ 有限支持,生产环境慎用)。











