为何使用std::memory_order_seq_cst时std::atomic读写结果不确定?
问题分析与解答
核心误区:混淆单线程程序顺序与线程间执行顺序
你对std::memory_order_seq_cst的理解存在偏差:它保证的是所有seq_cst原子操作在全局范围内形成一个单一的总执行顺序,同时保证单线程内的操作遵循程序顺序,但这并不意味着不同线程的操作会自动按你期望的先后执行。
你的代码中,主线程的load操作和子线程的store操作之间没有任何**happens-before(先行发生)**关系:
- 操作系统的线程调度是不确定的,主线程启动子线程后,可能立刻执行
load(此时子线程还未运行,store没执行),也可能子线程先完成store后主线程才执行load。这就是输出不确定的根本原因,和内存连贯性无关。
为什么seq_cst没起到你预期的作用?
std::memory_order_seq_cst的全局总序特性,只保证:如果在这个总序里,子线程的store排在主线程的load之前,那么load一定能读到1234;反之则读到0。但它不会强制约束这两个操作的先后顺序——线程调度的随机性依然会导致两种结果都可能出现。
如何确保读到预期的1234?
要让主线程的load一定能看到子线程的store,需要建立明确的happens-before关系,比如:
将
load放在t.join()之后:std::atomic<int> data(0); void func() { data.store(1234, std::memory_order_seq_cst); } int main() { std::thread t(func); t.join(); // 等待子线程完成,确保store先行发生于后续的load int val = data.load(std::memory_order_seq_cst); std::cout << "value: " << val << std::endl; return 0; }join()操作会确保子线程的所有操作(包括store)先行发生于主线程中join()之后的所有操作,因此load必然读到1234。使用额外的原子同步标志:
std::atomic<int> data(0); std::atomic<bool> ready(false); void func() { data.store(1234, std::memory_order_relaxed); ready.store(true, std::memory_order_seq_cst); } int main() { std::thread t(func); while (!ready.load(std::memory_order_seq_cst)) {} // 等待子线程完成store int val = data.load(std::memory_order_relaxed); std::cout << "value: " << val << std::endl; t.join(); return 0; }这里
ready的store和load建立了happens-before关系,确保data的store先行发生于data的load。
总结
- 单线程内的程序顺序是确定的,但不同线程的操作默认没有固定的执行顺序,需要显式同步机制(如
join、原子变量的同步关系、互斥量等)来建立happens-before约束。 std::memory_order_seq_cst保证全局总序,但不负责调度线程的执行顺序,它只是确保当操作的先后顺序被其他同步机制确定后,所有线程看到的操作顺序是一致的。
内容的提问来源于stack exchange,提问作者Benjamin Kim
相关产品推荐
相关产品推荐

