For AI agents: the complete documentation index is available at /tc39-atlas/llms.txt, the full documentation bundle is available at /tc39-atlas/llms-full.txt, and this page is available as Markdown at /tc39-atlas/proposals/year/2027/proposal-atomics-microwait.md.
  • 简体中文
  • Atomics.pause S4

    中文标题:Atomics.pause:JavaScript 中的微等待

    提案概览
    提案速览

    该提案解决了在 JavaScript 中高效实现自旋锁的难题,特别是在禁止阻塞的主线程上。它引入了 Atomics.pause() 这一新方法,该方法执行非常短暂的 CPU 级别让出,除时序外无可观察行为,从而提升自旋循环性能。

    Note

    以下 README 来自上游仓库,其中的阶段或状态标注可能滞后;当前信息以提案概览为准。

    JS 中的微等待

    阶段:第 3 阶段

    作者:郭舒羽

    提案负责人:郭舒羽

    动机

    高效实现锁时,获取锁的循环体通常采用以下形式:

    // 快速路径
    let spins = 0;
    do {
      if (TryLock()) {
        // 已获取锁。
        return;
      }
    
      SpinForALittleBit();
      spins++;
    } while (spins < kSpinCount);
    
    // 慢速路径
    PutThreadToSleepUntilLockReleased();

    该算法在竞争较低时速度很快,在竞争较高时效率也很高。当竞争较低(即锁在极短时间内释放的可能性很高)时,短暂自旋可以提高性能,因为代码无需重新进入内核来让出或休眠。当竞争较高(即锁在极短时间内释放的可能性很低)时,返回内核让执行线程休眠可以提高效率。

    SpinForALittleBit() 在 JavaScript 中无法最优地写出,因为最优版本通常需要向 CPU 提示以允许同级核心访问共享资源。例如,在 x86 上,Intel 优化手册 推荐使用带有 pause 指令和指数退避的循环。没有这种提示的版本会遭受性能和调度问题。

    PutThreadToSleepUntilLockReleased() 在主线程上无法最优地写出,因为在主线程上阻塞被策略性地禁止,以防止浏览器 UI 中的死锁和挂起。本提案不寻求解决这个用例

    用例:Emscripten

    Emscripten 在其主线程上的 futex 实现(用于实现互斥锁)中使用忙循环来模拟阻塞等待。

    允许微等待将提高使用 Emscripten 编译的多线程应用程序的功耗和调度效率。

    提案

    我建议在 Atomics 上新增一个方法。

    Atomics.pause

    为了更好地自旋,添加 Atomics.pause()。它执行一个非常短时间的有限等待,运行时可以使用适当的 CPU 提示来实现。除了时序之外,它没有其他可观察的行为。

    Atomics.wait 不同,由于它不阻塞,因此可以从主线程和工作线程调用。

    实现应遵循底层架构的最佳实践,实现一个带有 CPU 让出的短暂自旋。

    先前的讨论和致谢

    微等待在 SharedArrayBuffers 被提出时曾讨论过。参见 https://github.com/tc39/proposal-ecmascript-sharedmem/issues/87。在我看来,该线程中的论点至今仍然有效。`Atomics.pause` 基本上与 Lars 之前的设计完全相同。

    线程让出和高效自旋循环也曾在 WebAssembly 的上下文中讨论过。参见 https://github.com/WebAssembly/threads/issues/15。

    常见问题解答

    Atomics.pause() 会让出执行权给另一个线程吗?

    不会。微等待会在 CPU 内让出共享资源,但不会放弃核心本身的占用。线程让出在操作系统层面完成,而非 CPU 层面。

    为什么我不能阻塞主线程?

    阻塞主线程是不好的,因为它会对网页的响应性和性能产生灾难性影响。

    我仍然想阻塞主线程,我们能否限制 Atomics.wait 上的超时时间?

    最初,本提案包含一个 Atomics.wait 的重载,允许在主线程上将超时值限制为实现定义的上限。其想法是,如果阻塞时间足够短,并且某种程度上与实现认为的“空闲时段”对齐,那么禁止主线程阻塞的策略选择就不会被违反。

    出于以下原因,该重载已从提案中移除:

    • 收益不足。关于允许在主线程无限期阻塞没有共识,而有界时间阻塞对该用例的价值有限。
    • 在 Web 嵌入中难以指定_如何_限制超时。之前的想法是将其与当前的“空闲时段”联系起来,这取决于计划的任务和响应用户输入的事件处理程序等。然而,这在机制上很复杂。此外,应用程序中很可能_没有_空闲时段,这表明我们可能需要对限制设置下限和上限。这进一步加剧了规范制定的难度,而收益却很小。

    为什么 Atomics.pause 不接受可选参数?

    最初,本提案包含一个可选整数参数 N,用于控制暂停时间,较大的 N 值暂停更长时间。其想法是,当微等待本身在循环中时,它可以用于实现退避策略。

    出于以下原因,该参数已从提案中移除:

    • 没有任何实现使用它。已发布的实现接受该参数但并未根据它改变暂停持续时间,因此该参数对用户代码没有提供可观察的益处。
    • 发布一个未使用的参数会带来未来的“性能兼容性”约束。如果野外 JavaScript 代码在未测试效果的情况下为 N 传递值,那么后来选择遵循该参数的解释器可能会降低此类代码的性能。