atomic_thread_fence与atomic exchange交互:双线程能否出现双零值?
代码示例
std::atomic<int> x{0}; std::atomic<int> y{0}; Thread A: int y_val = y.load(std::memory_order_acquire); if (y_val != 1) { int old_x = x.exchange(1, std::memory_order_seq_cst); y_val = y.load(std::memory_order_acquire); } Thread B: y.store(1, std::memory_order_release); std::atomic_thread_fence(std::memory_order_seq_cst); int x_val = x.load(std::memory_order_relaxed);
问题
两个线程执行完上述代码后,是否可能出现y_val==0且x_val==0的情况?
结论与分析
不可能出现这种情况,具体分析如下:
核心逻辑梳理
线程A只有在第一次y.load(acquire)读到0时,才会进入分支执行x.exchange(1, seq_cst),随后第二次读取y的值;线程B先写入y=1(release语义),再执行一个seq_cst全局栅栏,最后读取x的值(relaxed语义)。
基于seq_cst语义的同步分析
seq_cst(顺序一致)语义要求所有seq_cst操作和seq_cst栅栏在全局范围内有一个统一的执行总序,这是关键约束:
情况1:线程A的
x.exchange(seq_cst)先于线程B的seq_cst栅栏
此时A已经将x的值改为1,由于seq_cst操作的全局可见性,线程B在栅栏之后的x.load(relaxed)必然能读到x=1,无法满足x_val==0的条件。情况2:线程B的seq_cst栅栏先于线程A的
x.exchange(seq_cst)
线程B的y.store(1, release)在栅栏之前执行,seq_cst栅栏会确保这个写操作对所有后续的读操作全局可见。线程A在x.exchange(seq_cst)之后执行的第二次y.load(acquire),必然能读到y=1,无法满足y_val==0的条件。
关于重排的补充说明
线程A中第二次y.load(acquire)和x.exchange(seq_cst)的写操作不会发生重排:x.exchange(seq_cst)本身包含acquire和release语义,且seq_cst操作不允许与后续的内存操作重排,因此第二次读取y的操作不可能被提前到exchange之前,也就不会读到未被更新的y=0。
内容的提问来源于stack exchange,提问作者emptysamurai

