You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

自旋锁为何循环执行30次_mm_pause()?相关实现合理性问询

问题解答

1. 为什么要执行30次_mm_pause()?CPU在这段时间做什么?

首先明确:_mm_pause()不是让CPU空闲,它是x86架构下的提示性指令,核心作用是告诉CPU当前处于自旋等待状态,需要调整流水线行为以降低性能损耗。

CPU执行_mm_pause()时的具体行为:

  • 占用少量指令周期(Intel CPU上约10-14个周期),这段时间CPU不会执行有效计算,但也不会进入休眠;
  • 抑制乱序执行与预取操作:若无pause指令,CPU会尝试预测后续指令并提前执行,而自旋等待中这类预测几乎都会失败,导致流水线清空、缓存失效,反而浪费更多资源;pause能让CPU避免此类无用功;
  • 降低功耗:相比空循环,pause会让CPU进入低功耗自旋状态,减少发热。

至于为什么是30次而非1次?这是实践总结的经验值:

  • 自旋锁的核心优势是:若锁的持有时间很短,自旋等待的开销远小于线程切换(涉及内核态调度、上下文保存);
  • 30次pause的总时长(约300-420个周期)刚好覆盖大多数短持有锁的释放窗口——比如很多临界区操作仅需几十到上百个周期,30次等待足够等到锁释放,又不会让CPU空转太久;
  • 若只执行1次,可能锁还未释放就放弃自旋触发线程切换,反而浪费了自旋的优势;次数过多则会在锁长时间持有时空转浪费CPU。

2. 基于C++协程的自旋锁实现是否合理?

这个实现的核心思路是可行的,但存在多处需要修正和优化的点:

合理的地方

  • 采用分阶段自旋+协程让出的策略:先尝试快速获取锁,再逐步增加pause等待时间,最后让出协程避免长时间空转,平衡了自旋的性能优势与CPU利用率;
  • 内存序选择正确:test_and_set使用std::memory_order_acquire符合自旋锁语义——获取锁后,能看到其他线程释放锁前的所有操作。

存在的问题与优化点

  1. std::atomic_flag初始化问题:std::atomic_flag默认处于未初始化状态,必须显式用ATOMIC_FLAG_INIT初始化,否则行为未定义。需补充构造函数:
    Spinlock() : m_flag(ATOMIC_FLAG_INIT) {}
    
  2. 快速路径重复操作:连续两次test_and_set完全冗余,可去掉一次;
  3. 协程让出的实现依赖:co_await Yield{}的效果完全依赖Yield的实现,必须确保它能让当前协程真正让出CPU,让线程执行其他任务,否则仍会空转;
  4. 缺少unlock函数:自旋锁必须配套释放逻辑,需添加:
    void unlock() {
        m_flag.clear(std::memory_order_release);
    }
    
    这里用memory_order_release保证释放锁前的所有操作对其他线程可见;
  5. pause次数的选择:1、3、5次的阶梯式等待可行,也可参考Go的固定次数(如30次)或指数退避策略,根据实际场景调整;
  6. 代码可读性优化:协程中用goto again虽合法,但改成while(true)循环结构会更易读。

优化后的简化示例:

#include <atomic>
#include <coroutine>

inline void cpu_pause() {
    _mm_pause();
}

template<unsigned int Times>
inline void cpu_pauses() {
    for (unsigned int i = 0; i < Times; ++i) cpu_pause();
}

struct Yield {
    bool await_ready() const noexcept { return false; }
    void await_suspend(std::coroutine_handle<> h) const noexcept {
        // 需结合协程调度器实现,将当前协程加入就绪队列以让出CPU
        // 示例:scheduler::schedule(h);
    }
    void await_resume() const noexcept {}
};

struct Spinlock {
    std::atomic_flag m_flag;

    Spinlock() : m_flag(ATOMIC_FLAG_INIT) {}
    Spinlock(const Spinlock&) = delete;
    Spinlock& operator=(const Spinlock&) = delete;

    struct promise_type {
        std::suspend_never initial_suspend() noexcept { return {}; }
        std::suspend_never final_suspend() noexcept { return {}; }
        void return_void() noexcept {}
        void unhandled_exception() noexcept { std::terminate(); }
        auto get_return_object() noexcept {
            return std::coroutine_handle<promise_type>::from_promise(*this);
        }
    };

    using async_void = std::coroutine_handle<promise_type>;

    async_void lock() {
        while (true) {
            // 快速路径
            if (!m_flag.test_and_set(std::memory_order_acquire)) {
                co_return;
            }

            cpu_pauses<1>();
            if (!m_flag.test_and_set(std::memory_order_acquire)) co_return;

            cpu_pauses<3>();
            if (!m_flag.test_and_set(std::memory_order_acquire)) co_return;

            cpu_pauses<5>();
            if (!m_flag.test_and_set(std::memory_order_acquire)) co_return;

            // 让出协程
            co_await Yield{};
        }
    }

    void unlock() {
        m_flag.clear(std::memory_order_release);
    }
};

内容的提问来源于stack exchange,提问作者likecpp

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.26 04:03:12