该传递性happens-before用例需顺序一致性还是仅需释放/获取语义?
关于Herb Sutter《Atomic Weapons》幻灯片的内存序疑问
以下内容来自Herb Sutter《Atomic Weapons》演讲第19页幻灯片:原示例使用std::memory_order_seq_cst保证线程3中的assert()不会失败,现疑问改用release/acquire语义是否同样可行。
原代码(顺序一致性)
int g{0}; // normal int std::atomic<int> x{0}, y{0}; // atomics void thread1() { g = 1; x.store(1, std::memory_order_seq_cst); } void thread2() { if(x.load(std::memory_order_seq_cst) == 1) y.store(1, std::memory_order_seq_cst); } void thread3() { if(y.load(std::memory_order_seq_cst) == 1) assert( g == 1 ); }
改写代码(释放/获取语义)
int g{0}; // normal int std::atomic<int> x{0}, y{0}; // atomics void thread1() { g = 1; // A x.store(1, std::memory_order_release); } void thread2() { if(x.load(std::memory_order_acquire) == 1) y.store(1, std::memory_order_release); } void thread3() { if(y.load(std::memory_order_acquire) == 1) assert( g == 1 ); // B }
疑问
- Q1:由于没有其他线程写入g,//A 是否能保证在//B之前发生,从而让断言有效?
- Q2:是否误解了幻灯片的主旨,或是幻灯片存在错误?
解答
针对Q1
断言可以保证有效。根据C++内存模型规则:
- 线程1中,
g=1(标记A)happens-beforex.store(1, release)——同一线程内操作按程序顺序形成天然的happens-before关系,且release语义会阻止A被重排到store之后。 - 若线程2中
x.load(acquire)读到1,则x.store(release)与x.load(acquire)形成synchronizes-with关系,进而线程2中的y.store(1, release)happens-afterx.load(acquire)。 - 若线程3中
y.load(acquire)读到1,则y.store(release)与y.load(acquire)形成synchronizes-with关系,进而assert(g==1)(标记B)happens-aftery.load(acquire)。
通过这一完整同步链条,A对g的写入对B完全可见,且没有其他线程修改g,因此g的值必然为1,断言不会触发。
针对Q2
既没有误解幻灯片主旨,也不存在幻灯片错误。幻灯片使用seq_cst是因为它是最直观的强一致性内存序,能保证所有原子操作全局有序,适合作为基础示例展示线程同步的正确性。而release/acquire是更轻量的内存序,在这个场景中,我们只需要构建线程1→线程2→线程3的单向同步链条,不需要全局顺序一致性,因此release/acquire完全可以满足需求,同样能保证断言不失败。
内容的提问来源于stack exchange,提问作者Dhwani Katagade
相关产品推荐
相关产品推荐

