基于原子变量的线程间同步中全局变量读取的正确性疑问
基于原子变量的线程间同步中全局变量读取的正确性疑问
嘿,这个问题问得很接地气,咱们先把你的代码完整整理出来,再一步步拆解分析:
#include <atomic> #include <thread> std::atomic<int> counter = 0; // 默认memory_order_seq_cst int globalInt1; int globalInt2; int globalInt3; void Wait() { /* 假设这里是不影响同步逻辑的等待操作 */ } void thread1() { globalInt1 = 1; --counter; Wait(); } void thread2() { globalInt2 = 2; --counter; Wait(); } void thread3() { globalInt3 = 3; --counter; Wait(); } int main() { ++counter; ++counter; ++counter; std::thread{ thread1 }.detach(); std::thread{ thread2 }.detach(); std::thread{ thread3 }.detach(); while (counter != 0); // 循环退出后读取globalInt1~3是否正确? }
你的核心疑问是:main线程等到counter变为0后,能不能确保正确读到globalInt1、globalInt2、globalInt3的赋值结果,还是说只能和最后修改counter的那个线程同步?
答案是:你完全可以在main线程里正确读取到三个全局变量的赋值结果,不会出现只能和最后一个修改counter的线程同步的情况,具体原因如下:
- 首先,
std::atomic<int>默认的内存序是memory_order_seq_cst,这种内存序会给所有原子操作强制一个全局总顺序——也就是说,所有线程看到的counter的增减操作顺序是完全一致的,不存在各线程看到不同操作顺序的情况。 - 其次,在每个工作线程内部,
globalIntX = X的赋值操作,和对应的--counter原子操作之间存在**happens-before(先行发生)**关系:同一个线程里,按代码顺序执行的操作,前面的操作必然先行发生于后面的操作,再加上memory_order_seq_cst的约束,编译器和CPU都不会对这些操作进行重排序。 - 然后,main线程里的三次
++counter操作,先行发生于三个工作线程的启动(因为main线程先执行完所有递增,才创建并启动工作线程);而只有当三个工作线程的--counter操作全部执行完毕后,counter才会回到0,main线程的循环才会退出。
根据C++内存模型的传递性规则:如果操作A先行发生于B,B先行发生于C,那么A先行发生于C。这里每个globalIntX = X先行发生于对应的--counter,所有--counter又先行发生于main线程看到counter == 0的读取操作,因此每个全局变量的赋值操作,都先行发生于main线程后续对它们的读取操作。这就保证了main线程读取到的一定是三个工作线程赋值后的正确值,不会是未初始化的垃圾值或者旧值。
你担心的“只能和最后修改counter的线程同步”的情况,不会在memory_order_seq_cst的场景下出现——全局总序确保了所有原子操作的顺序都被全局可见,先行发生关系的传递性也覆盖了所有工作线程的赋值操作到main读取的完整路径。
内容来源于stack exchange
相关产品推荐
相关产品推荐

