关于std::atomic同步问题的两个技术疑问
关于C++原子变量同步的两个问题解答
问题1:为何有人称必须用memory_order_seq_cst才能让示例正常工作?acquire的作用是什么?
首先明确:那个示例里用memory_order_acquire和memory_order_release完全能保证正确性,不需要强制用seq_cst。有人这么说大概率是两个误解:
- 混淆了「同步关系」和「全局全序」:
seq_cst会给所有原子操作强加一个单一的全局执行顺序,这是最严格的内存顺序,但很多场景下根本不需要这个全局顺序。而acquire/release只需要保证有同步关系的线程对之间的可见性和顺序,足够满足示例的需求——只要线程2看到flag被设置为true,就能确保线程1在设置flag前的所有写入都可见。 - 对acquire的可见性范围理解偏差:cppreference说的「其他线程中对同一原子变量执行释放操作的所有写入对当前线程可见」,是指该release操作之前的所有写入(包括普通变量),且仅针对建立了同步关系的线程对,不是全局所有线程的写入都能随便可见。
至于memory_order_acquire的核心作用:
- 阻止当前线程中,所有后续的读、写操作被重排到这个加载操作之前;
- 和对同一原子变量的
memory_order_release操作建立happens-before关系,确保release线程在release之前的所有内存写入(不管是原子还是普通变量),对执行acquire的线程都可见。它是轻量级的同步手段,性能比seq_cst好很多,适合不需要全局全序的场景。
问题2:为何示例绝不会输出"bad\n"?
拿示例的核心逻辑来说:
线程1先给普通变量data赋值42,再用release语义把原子变量flag设为true;线程2循环用acquire语义加载flag,直到读到true后检查data的值。
这里的关键是acquire和release建立的happens-before关系:
release语义会阻止线程1里的任何读写操作(包括data=42)被重排到flag.store之后,所以data=42一定在flag.store之前完成;- 当线程2的
flag.load返回true时,说明它看到了线程1的flag.store操作,此时两者的同步关系生效:线程1中flag.store之前的所有操作(包括data=42)都happens-before线程2中flag.load之后的所有操作(包括读取data)。
这就意味着,线程2读取data时,必然能看到线程1写入的42,自然永远不会输出"bad"。
内容的提问来源于stack exchange,提问作者fyou
相关产品推荐
相关产品推荐

