SharedArrayBuffer + Atomics 是 Node.js 多 Worker 下高效无锁计数的原生方案,主进程创建并转移 SharedArrayBuffer,Worker 须用 Atomics.add 等原子操作读写,避免竞态。

SharedArrayBuffer + Atomics 是 Node.js 多 Worker 场景下实现高效无锁计数的原生方案。它不依赖 Redis 或数据库,无需加锁、无网络开销,所有操作在内存中完成,延迟低至纳秒级。
核心前提:主进程统一创建并分发共享内存
每个 Worker 的 V8 实例内存完全隔离,模块变量无法共享。必须由主进程(Master)创建 SharedArrayBuffer,并通过 postMessage 将 buffer 本身转移过去(不是拷贝):
- 主进程创建
new SharedArrayBuffer(4)(4 字节足够存一个 32 位整数) - 用
new Int32Array(sab)创建视图,作为共享计数器载体 - 调用
worker.postMessage(sab, [sab])—— 注意 transfer list [sab] 不可省略,否则传的是副本
Worker 中必须用 Atomics 原子操作递增
普通赋值(如 counter[0]++)会触发“读-改-写”三步,多 Worker 并发时必然丢失更新。唯一安全方式是使用 Atomics.add():
-
Atomics.add(counterView, 0, 1)是单条原子指令,底层映射为 CPU 的 LOCK XADD,天然线程安全 - 不需要 if 判断、不需要 while 重试、不需要额外锁或信号量
- 返回值是递增前的原始值,可用于条件逻辑(例如限流判断:
if (Atomics.add(counterView, 0, 1) < 1000))
读取与验证:避免竞态的只读访问
读取计数值也必须用 Atomics.load(),不能直接访问 counterView[0]:
-
Atomics.load(counterView, 0)保证读取动作本身不可中断,且能获取其他 Worker 刚写入的最新值 - 若需“读-判-写”复合逻辑(如“仅当小于阈值才加一”),应改用
Atomics.compareExchange()实现乐观更新 - 示例:防止超限递增
let cur; while ((cur = Atomics.load(counterView, 0)) < 1000 && Atomics.compareExchange(counterView, 0, cur, cur + 1) !== cur);
注意事项与常见避坑点
该方案轻量但对使用方式敏感,几个关键细节决定成败:
- Node.js 需启用标志:
node --experimental-shared-array-buffer server.js - 必须用
Int32Array(或BigInt64Array)视图;Float64Array等不支持 Atomics 操作 - 所有 Worker 必须基于同一块
sab实例映射视图,不能各自 new - 不要在主线程(主进程)中调用
Atomics.wait(),该方法仅限 Worker 内使用 - 初始化很重要:首次写入建议用
Atomics.store(counterView, 0, 0)显式设初值

















