x86-64 中 MFENCE 完成后,其他核心稍后执行的 LOAD 必须读到新值吗?
如果题设中的“随后执行 LOAD”确实表示 T1 的 MFENCE 已完成,且 T2 的 LOAD X 此前尚未读取该位置,那么在普通、保持缓存一致性的 WB 内存上,没有其他写入的前提下,T2 必须读到 X = 1。T2 不需要再执行一次 MFENCE 才能看到这个值。
这里需要明确“执行”的含义。LOAD 实际读取内存的时刻,可能不同于指令退休或程序记录日志的时刻。仅凭后两者的时间顺序,不能直接得出题设中的前提。
MFENCE 的全局可见性意味着什么?
问题引用的 Intel 手册说明,MFENCE 会按要求对其前后的内存访问排序。手册同时指出,MFENCE 不会序列化整个指令流。对于 LOAD,“全局可见”表示将要装入目标寄存器的值已经确定。Intel 指令手册:MFENCE
在 x86-TSO 的抽象机器中,每个线程都有自己的 FIFO store buffer。STORE 可以先进入缓冲区,随后传播到共享内存;MFENCE 只有在本线程的 store buffer 为空时才能通过。其他线程读取某个地址时,如果没有可转发的本地写入,就从抽象的共享内存取值。x86-TSO 论文,第 3 节
将这些规则用于简化示例,可以得到:
STORE X = 1 传播到全局一致性域
→ T1 的 MFENCE 完成
→ T2 的 LOAD X 实际读取
在这一顺序下,如果没有后续写入,最后一步应当返回 1。这是上述模型在题设顺序下的直接推论。
“全局可见”也不要求把数据写回 DRAM,或主动把 1 填进每个核心的缓存。数据可以留在缓存层次中,一致性机制负责使后续读取获得正确的值。
为什么稍后退休的 LOAD 仍可能返回旧值?
“稍后退休”没有说明 LOAD 是什么时候取值的。例如,以下时间线与题设中的“稍后才开始读取”不同:
T2 的 LOAD X 提前执行,取得 0
T1 的 STORE X = 1 成为全局可见
T1 的 MFENCE 完成
T2 的 LOAD X 退休,提交先前取得的 0
这里的 LOAD 在写入全局可见之前已经读取。它稍后退休,并不会因此自动重新读取内存。
同理,源代码顺序、日志输出时间或缺少充分排序约束的计时结果,都不能直接代替实际内存访问顺序。处理器可能提前执行指令;最终允许保留哪些结果,还必须满足架构规定的内存顺序。
这并不意味着,真正发生在全局可见写入之后的读取仍可任意返回旧缓存值。要区分 LOAD 的取值发生在写入全局可见之前还是之后。
为什么第一组程序不能同时读到 0?
将原始示例对齐,并给两个 LOAD 的结果命名:
T1: T2:
STORE X = 1 STORE Y = 1
MFENCE MFENCE
r1 = LOAD Y r2 = LOAD X
假设 X、Y 初始为 0,之后各只有这一次写入。
两条 MFENCE 分别要求:
STORE X = 1 → r1 = LOAD Y
STORE Y = 1 → r2 = LOAD X
如果 r1 = 0,读取 Y 就必须排在 STORE Y = 1 全局可见之前;如果 r2 = 0,读取 X 也必须排在 STORE X = 1 全局可见之前。把这些约束连接起来:
STORE X = 1
→ LOAD Y
→ STORE Y = 1
→ LOAD X
→ STORE X = 1
这些约束形成了顺序环,因此两个结果不能同时为 0。证明依据的是内存顺序约束,不需要先测出两个核心各条指令的物理时间。
如何在程序中建立可观察的先后关系?
原问题已经明确假设时间顺序成立,因此回答该假设不需要额外同步。如果实际程序需要建立这种关系,可以发布一个标志,让读取线程根据标志继续执行:
# 初始值:X = 0,ready = 0
T1:
STORE X = 1
MFENCE
STORE ready = 1
T2:
retry:
r = LOAD ready
if r == 0: goto retry
value = LOAD X
在相同的普通 WB 内存前提下,T2 读到 ready = 1 后,再读取 X 必须得到 1。在这种发布模式中,x86 的 Store → Store 和 Load → Load 顺序已足以提供所需约束。示例保留 MFENCE,是为了标记原问题所关心的完成阶段。
如果用 C 或 C++ 实现,应使用语言规定的原子操作和同步机制,例如 release/acquire 发布标志。单独插入硬件屏障不能替代语言层面的并发规则。
适用前提
以上分析针对同一物理内存位置、正常一致性域内的普通 WB 内存访问,并假设访问具备所需的原子性,没有其他线程再次修改 X。MMIO、特殊内存类型、不一致的内存映射或非原子访问,需要另外分析。
跨线程的真实时间推理要区分实际先后关系是否已确定。题设明确给定该关系时,可以得出 LOAD X 返回 1;如果程序只有这几条指令,且没有建立先后关系,T2 仍可能在写入全局可见之前读到 0。
备注:内容仅供参考。
