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

x86-64汇编实现自旋锁仅首个线程进入临界区问题咨询

问题原因

你的自旋锁实现存在两个核心错误,该现象和多核心下的缓存一致性机制直接相关:

  1. 锁获取操作不具备原子性
    你使用shrb $1,.L0C_A1(%rip)作为锁的抢锁逻辑,这是典型的读-改-写(RMW)操作。x86架构下,普通RMW指令默认不会在多核心环境下保证原子性,多个线程可以同时读取到锁值为1的状态,分别移位后同时写回0,最终锁值变为0,但多个线程都认为自己抢到了锁,会引发临界区并发问题。
  2. 自旋阶段存在写风暴
    你将带写操作的shrb直接放在自旋循环中,所有等待锁的线程会不断对锁所在的内存地址执行写操作。每个写操作都需要先获取对应缓存行的独占所有权,大量并发的RFO(所有权请求)会导致锁所在的缓存行在多个核心之间反复弹跳,不仅性能极低,还会导致持有锁的核心释放锁的写操作被长时间阻塞。甚至会出现锁刚被释放为1,就立刻被等待线程的shrb改写为0,由于原子性缺失,等待线程无法正确捕获到这次锁空闲的状态,最终表现为只有第一个线程能进入临界区。

为什么GDB断点后可以正常运行

当你在释放锁的指令处设置断点时,GDB会暂停所有线程的执行,你单步执行完movb $1,.L0C_A1后,锁已经被成功设置为1,此时再恢复所有线程运行,没有了持续的写操作争抢缓存行,线程可以正常读取到锁的空闲状态,因此可以正常抢锁进入临界区。


修复方案

1. 给RMW操作加lock前缀保证原子性

lock前缀会锁总线/锁缓存行,保证读改写操作的原子性,修改抢锁指令为:

lock shrb    $1,.L0C_A1(%rip)

2. 优化自旋逻辑,避免自旋阶段写操作

先通过只读判断检测锁是否空闲,只有锁空闲时再执行原子抢锁操作,大幅减少缓存行争抢:

func:
spin_wait:
    cmpb    $1, .L0C_A1(%rip)  # 只读判断锁是否空闲
    jne     spin_wait          # 锁被占用就继续自旋
    lock shrb $1,.L0C_A1(%rip) # 锁空闲时才尝试原子抢锁
    jnc     spin_wait          # 抢锁失败回到自旋阶段

    # 原有临界区代码不变
    movq    x(%rip),%rax

    xorl    %esi,%esi
    pushq   $10000000
    pushq   %rsi
    movq    %rsp,%rdi
    pushq   %rax
    leal    35(%rsi),%eax
    syscall                   # sleep 10 ms
    pop     %rax
    addq    $16,%rsp

    xorl    %edx,%edx
    mulq    %rax              # x = x * x
    movq    %rax,x(%rip)      # store new x
    movb    $1,.L0C_A1(%rip)  # leave critical section
    xorl    %eax,%eax
    ret

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.25 18:54:02