关于std::memory_order_consume与happens-before关系的逻辑矛盾疑问
关于std::memory_order_consume的happens-before关系推导疑问
首先给出示例代码:
struct X { int i; std::string s; }; std::atomic<X*> p; std::atomic<int> a; void create_x() { X* x=new X; x->i=42; x->s="hello"; a.store(99,std::memory_order_relaxed); //1 p.store(x,std::memory_order_release); //2 } void use_x() { X* x; while(!(x=p.load(std::memory_order_consume))) //3 std::this_thread::sleep(std::chrono::microseconds(1)); assert(x->i==42); //4 assert(x->s=="hello"); // 5 assert(a.load(std::memory_order_relaxed)==99); // 6 } int main() { std::thread t1(create_x); std::thread t2(use_x); t1.join(); t2.join(); }
Anthony Williams在《Concurrency in Action》中提到:尽管对a的存储(标记1)先于对p的存储(标记2),且p的存储用了memory_order_release,但p的加载(标记3)用了memory_order_consume,仅能保证依赖于p加载值的表达式(比如断言4、5中访问X的成员)不会触发失败,而断言6是可能触发失败的。
但按照cppreference的happens-before关系推导,你得出了矛盾的结论:
- 存储1和存储2存在sequenced-before和happens-before关系;
- 存储2和加载3存在dependency-ordered-before和inter-thread-happens-before关系;
- 结合后认为存储1 inter-thread-happens-before加载3,进而1 happens-before 3;
- 加载3和加载6存在sequenced-before,故3 happens-before 6;
- 通过传递性得出1 happens-before 6,这和书中结论矛盾。
你的推导逻辑问题出在哪?
核心错误是第三步对inter-thread-happens-before传递性的误解:
memory_order_consume建立的inter-thread-happens-before关系,只对依赖于加载结果的操作生效。也就是说,只有当某个操作的执行依赖于p.load()的返回值时,这个操作才能看到p.store()(release)之前的所有操作。- 加载
a的操作(标记6)和p.load()的结果没有任何依赖关系——不管p.load()返回什么值,都会执行a.load()。所以存储1和加载6之间并不存在有效的happens-before传递链。
为什么happens-before的传递性在这里不适用?
C++标准中,inter-thread-happens-before的传递需要满足依赖链的连续性。memory_order_consume的设计目标就是轻量级同步:它只保证那些依赖于原子加载结果的操作能看到release端的前置操作,不保证无关操作的可见性。编译器或CPU完全可以重排加载6和加载3的顺序,或者让加载6看不到存储1的结果——因为两者没有依赖关系,这种重排不违反内存模型规则。
简单说:memory_order_consume的同步范围是依赖于加载值的操作子集,不是整个线程后续的所有操作。所以断言6确实可能失败,和书中结论一致。
内容的提问来源于stack exchange,提问作者mentalmushroom
相关产品推荐
相关产品推荐

