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

锁获取后是否需要内存屏障?基于多CPU无缓存系统的技术疑问

自旋锁内存屏障的正确性分析

系统与代码背景

  • 硬件环境:3颗共享内存和总线的CPU,无缓存但配备存储缓冲区
  • 指令支持:原子CAS指令;三种内存屏障:
    • LoadMemoryBarrier():阻止加载操作跨屏障重排
    • StoreMemoryBarrier():阻止存储操作跨屏障重排
    • GeneralMemoryBarrier():阻止所有内存访问跨屏障重排
  • CPU重排规则:可任意重排存储与存储/加载、加载与存储/加载操作,以及这些操作与原子操作的顺序
  • 语言标准:C99,无法使用stdatomic库,CAS无隐式内存屏障

示例代码

unsigned int spinlock = 0;
int x = 0;
void lock()
{
    while (CAS(&spinlock, 1, 0) == 1); // Wait to acquire lock
    // Spinlock acquired
    LoadMemoryBarrier();
}

void unlock()
{
    GeneralMemoryBarrier();
    spinlock = 0;
}

void CPU1()
{
    lock();
    x += 1;
    unlock();
}

void CPU2()
{
    lock();
    x -= 1;
    unlock();
}

void CPU3()
{
    printf("%d", x);
}

用户疑问

对x的存储操作是否可能被重排到自旋锁获取之前?认为不会,因此lock()函数内只需LoadMemoryBarrier()即可,该理解是否正确?能否给出该屏障不足以保证的场景?


回答

你的理解不正确,对x的存储操作确实可能被重排到自旋锁获取之前,LoadMemoryBarrier()无法满足自旋锁的内存语义要求,具体问题场景如下:

1. 重排风险的核心原因

LoadMemoryBarrier()仅对加载操作的重排有约束,完全不限制存储操作的重排行为。而CPU允许存储操作(比如x +=1对应的最终存储)与原子CAS指令之间的重排,因此临界区内的存储操作完全可能被CPU重排到CAS成功获取锁之前执行。

2. 具体错误执行场景

假设CPU1、CPU2、CPU3按以下顺序执行:

  • CPU1发起CAS尝试获取锁,此时spinlock为0,CAS执行成功(将spinlock设为1),但CPU的存储缓冲区将x +=1的存储操作重排到CAS之前执行,直接把x的值从0修改为1。
  • 此时CPU3执行printf("%d", x),读取到x=1。
  • 随后CPU2开始执行lock(),因spinlock为1进入循环等待;CPU1执行unlock()中的GeneralMemoryBarrier()和spinlock=0操作,释放锁。
  • CPU2检测到spinlock为0,CAS获取锁后执行x -=1,将x修改为0。

这个场景中,CPU1对x的修改是在未持有锁的状态下完成的,完全违背了自旋锁的互斥语义——自旋锁的核心作用是保证临界区操作必须在持有锁期间执行,而当前的LoadMemoryBarrier()无法阻止这种破坏互斥性的重排。

3. 正确的lock()实现

在CAS成功获取锁后,需要插入StoreMemoryBarrier()或GeneralMemoryBarrier(),而非LoadMemoryBarrier()。StoreMemoryBarrier()可以确保屏障之后的所有存储操作(临界区内对x的修改)不会被重排到屏障之前的CAS操作之前,从而保证临界区操作严格在锁持有期间执行。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.12 19:08:32