使用宽松原子操作的比较并交换(Compare-and-Swap)问题
原子操作内存顺序问题分析
示例代码
void foo(atomic<int> &x) { int i = 0; bool success = x.compare_exchange_strong(i, i + 2, memory_order_relaxed, memory_order_relaxed); assert(!success); } void bar(atomic<int> &x) { x.store(1, memory_order_relaxed); } int main() { atomic<int> x = 0; auto bar_thread = thread(bar, std::ref(x)); sleep(1); auto foo_thread = thread(foo, std::ref(x)); sleep(1); foo_thread.join(); bar_thread.join(); }
问题描述
尽管代码中使用了
sleep,但线程仍可能交错执行,不过可假设本示例中bar线程在foo线程执行前已完成。请问此场景下能否保证assert永远不触发?我的理解是:
x.store(1, memory_order_relaxed)虽是原子操作,但写入内容可能无法立即被其他线程感知,除非搭配acquire-release或顺序一致性内存顺序。我为compare_exchange_strong的失败参数传入memory_order_relaxed,理论上它可能读取x的初始值0,但实际assert从未触发(我知道这不代表必然结果)。想请教:无论传入何种内存顺序,
compare_exchange_strong最终都会与最后一次写入同步吗?我猜测x的初始值以memory_order_relaxed读取,仅当值与expected相等时,才执行更耗时的比较交换操作?
解答
结论
在假设bar线程完全在foo线程执行前完成的前提下,能保证assert永远不会触发。
关键解释
- 原子操作的最终可见性:哪怕是
memory_order_relaxed内存顺序,原子操作的核心特性是全局可见性的最终一致性。只要一个线程的原子写入操作完全完成,其他线程后续对该原子变量的任何原子读取(包括compare_exchange_strong中的读取动作)最终一定会看到这个写入值,不会永久停留在旧值。你提到的"无法立即感知"只是指relaxed不保证跨线程操作的顺序性,但不代表写入会永远不可见。 - compare_exchange_strong的原子性逻辑:该操作是原子性地完成"读取当前值→与expected比较→条件写入"的整个流程,不存在先读取旧值再用旧值执行后续比较的情况。在本场景中,foo线程启动时bar的写入已经完成,
compare_exchange_strong读取到的x值必然是1,和expected的0不相等,因此返回false,assert不会触发。 - 内存顺序参数的作用:内存顺序影响的是跨线程操作之间的同步关系(比如是否让其他内存操作的可见性随之同步),但不会改变原子操作本身对目标变量的读写原子性,也不会阻止已完成的原子写入被后续原子读取看到。无论使用哪种内存顺序,只要bar的写入完全先于foo的操作,foo就能读到bar写入的1。
内容的提问来源于stack exchange,提问作者petabyte
相关产品推荐
相关产品推荐

