Spinlocks vs. Mutexes:何时自旋,何时休眠

发布: (2025年12月8日 GMT+8 08:38)
4 min read

Source: Hacker News

引言

你正盯着 perf top,看到 60 % 的 CPU 时间花在 pthread_mutex_lock 上。延迟糟透了。有人建议“直接用自旋锁”,结果你的 16 核服务器瞬间 100 % 占用,却什么也没干。这就是同步原语的陷阱:工程师常因为不了解各自的适用场景而选错原语。

自旋锁 vs. 互斥锁

属性互斥锁自旋锁
行为发生争用时会睡眠。在用户态忙等(自旋)。
无争用时的开销25–50 ns(快速路径)一次原子 LOCK CMPXCHG(≈40–80 ns)。
有争用时的开销系统调用 (futex(FUTEX_WAIT)) ≈ 500 ns + 上下文切换(3–5 µs)。循环时占用 100 % CPU;每次失败的尝试都会在核心之间来回抖动缓存行。
可抢占的上下文安全——线程可以被调度下去。危险——如果持锁者被抢占,其他线程会浪费整整一个时间片。
优先级反转可通过优先级继承(PI)互斥锁解决。无法解决;高优先级线程可能永远自旋,而低优先级持锁者根本得不到运行机会。
伪共享同样的问题——每次原子操作都会使缓存行失效。同样的问题——需要额外注意对齐锁结构。

何时使用哪种原语

临界区持续时间争用程度推荐原语
** 10 µs** 或 高争用常规互斥锁——让调度器处理睡眠/唤醒。
实时需求任意在 PREEMPT_RT 内核上使用优先级继承互斥锁。

性能分析技巧

  • CPU 与上下文切换 – 运行:

    perf stat -e context-switches,cache-misses 
    • 高上下文切换计数且 CPU 使用率低 → 互斥锁开销可能占主导。
    • 高缓存未命中且 CPU 100 % → 锁争用 / 伪共享。
  • 系统调用计数 – 使用 strace -c 统计 futex() 调用。每秒数百万次表明该锁争用激烈,可能需要分片或无锁技术。

  • 自愿 vs. 非自愿切换 – 查看 /proc/<pid>/status。在持自旋锁期间出现非自愿切换,说明存在抢占问题。

实际案例

  • Redis – 为其小型任务队列使用自旋锁(临界区)。

    #include <pthread.h>
    #include <stdatomic.h>
    
    #define NUM_THREADS   4
    #define ITERATIONS    1000000
    #define HOLD_TIME_NS  100   // 模拟工作
    
    typedef struct {
        atomic_int lock;
        long       counter;
    } spinlock_t;
    
    static void spinlock_acquire(spinlock_t *s) {
        int expected;
        do {
            expected = 0;
        } while (!atomic_compare_exchange_weak(&s->lock, &expected, 1));
    }
    
    static void spinlock_release(spinlock_t *s) {
        atomic_store(&s->lock, 0);
    }
    
    static void *worker_thread(void *arg) {
        spinlock_t *s = (spinlock_t *)arg;
        for (long i = 0; i < ITERATIONS; i++) {
            spinlock_acquire(s);
            s->counter++;
            /* 模拟约 100 ns 的工作 */
            for (volatile int j = 0; j < HOLD_TIME_NS; j++) {
                // busy‑wait
            }
            spinlock_release(s);
        }
        return NULL;
    }

插图

Spinlock vs. Mutex performance diagram

Profiling context switches and cache misses

Back to Blog

相关文章

阅读更多 »

规划我的下一个开源贡献

背景 在过去的一段时间里,我更加积极地参与开源项目,尤其是与 TypeScript 生态系统相关的项目。在我的 pull request…