Atomics.pause()

基准线 2025
新推出

自 ⁨2025 年 4 月⁩起,此功能适用于最新设备和浏览器版本。此功能可能不适用于较旧的设备或浏览器。

Atomics.pause() 静态方法提供了一种微等待原语,它提示 CPU 调用者正在忙等待(spinning)以访问共享资源。这允许系统减少分配给核心(如功耗)或线程的资源,而无需让出当前线程。

除了计时外,pause() 没有其他可观察的行为。具体行为取决于 CPU 架构和操作系统。例如,在 Intel x86 上,它可能是一个 pause 指令,根据 Intel 的优化手册。在某些平台上,它可能是一个空操作(no-op)。

语法

js
Atomics.pause()
Atomics.pause(durationHint)

参数

durationHint 可选

一个实现可以用来确定等待时间的整数。对于值 n + 1,实现等待的时间至少与给定值 n 的时间一样长。具体数字没有实际意义。在纳秒到几十纳秒的量级上,可能存在内部最大暂停时间的上限。这可以用来通过增加传递的 durationHint 来实现 退避策略。不能保证实现一定会利用此提示。

返回值

无(undefined)。

异常

TypeError

如果 durationHint 不是整数或 undefined,则抛出错误。

示例

使用 Atomics.pause()

调用 Atomics.wait()Atomics.waitAsync() 来等待访问共享内存会导致线程被调度出核心,并在等待结束后重新调度回核心。在高争用时期,这非常高效,因为访问共享内存可能需要一些时间。当争用较低时,通常更有效的方法是在不让出线程的情况下轮询锁:这种方法称为 忙等待自旋锁pause() 方法通过向 CPU 提供线程正在做什么的提示,从而降低其对资源的需求,使您能够更有效地自旋锁等待。

为了兼顾这两种情况,一种常见的方法是首先尝试自旋锁,希望能快速获取锁(争用低),然后在一段时间后如果仍未获得锁,则进行等待。如果我们已经通过自旋锁获取了锁,那么 wait() 调用将是一个空操作。

下面的示例显示了如何将这种方法与 Atomics.pause()Atomics.wait() 结合使用。

警告:不建议在主线程上使用自旋锁,因为它会冻结整个页面。总的来说,除非经过非常仔细的设计,否则自旋锁的性能可能并不比常规等待更好。

js
// Imagine another thread also has access to this shared memory
const sab = new SharedArrayBuffer(1024);
const i32 = new Int32Array(sab);

// Fast path: spin the CPU for a short while
let spin = 0;
do {
  if (Atomics.compareExchange(i32, 0, 0, 1) === 0) {
    break;
  }
  Atomics.pause();
  spin++;
} while (spin < 10);

// Slow path: wait for the lock
// This can only be called in a worker thread,
// because the main thread cannot be blocked
Atomics.wait(i32, 0, 1);

退避策略

durationHint 参数可用于实现退避策略。例如,线程可以从一个小的提示开始,并在每次迭代中指数级增加提示。这比多次调用 pause() 要好,因为在未 JIT 编译的代码中,函数调用本身就有很高的开销。

注意:实现可能根本不使用 durationHint,而是始终等待一个固定的时间。

js
// Exponential backoff
for (let hint = 1; hint < 1000; hint *= 2) {
  Atomics.pause(hint);
}

// Linear backoff
for (let hint = 1; hint < 100; hint++) {
  Atomics.pause(hint);
}

规范

规范
Atomics.pause
# Atomics.pause

浏览器兼容性

另见