std::memory_order_seq_cst全局单序机制解析及ARM64平台异常结果问询
最近在ARM64架构的Mac上跑一段C++原子变量测试代码,结果遇到了和预期不符的情况,想请教一下各位大佬问题出在哪。
测试背景与预期
我的测试逻辑很直接:有两个独立的std::atomic<int>变量x和y,初始值都是0,之后会被分别设置为1;同时启动多个读取线程,每个线程读取x和y的当前值。
根据C++标准中std::memory_order_seq_cst的**全局单序(single global order)**语义,理论上不应该同时出现{x=0,y=1}和{x=1,y=0}这两种结果——因为全局单序要求所有seq_cst操作都遵循一个统一的全局顺序:要么x=1的写入先于y=1,要么反过来,这样所有线程看到的结果应该保持一致,不会同时出现这种交叉的情况。
为了同步所有线程的启动时机,我用了std::atomic<bool> ready作为触发信号:所有线程必须等ready被设为true后才开始执行读写操作。而且所有原子操作我都指定了std::memory_order_seq_cst内存序,按道理应该严格遵循全局单序才对,但在我的ARM64 Mac上,确实出现了这两种交叉结果同时存在的情况。
测试代码
#include <atomic> #include <iostream> #include <thread> #include <vector> const char *machine() { std::cout << "Compiled on/for: "; #if defined(__x86_64__) || defined(_M_X64) || defined(_M_AMD64) return "x86-64 (64-bit Intel/AMD)"; #elif defined(__i386__) || defined(_M_IX86) return "x86 (32-bit Intel/AMD)"; #elif defined(__aarch64__) || defined(_M_ARM64) return "ARM64 (AArch64)"; #elif defined(__arm__) || defined(_M_ARM) return "ARM (32-bit)"; #elif defined(__powerpc__) || defined(__powerpc64__) || defined(_M_PPC) return "PowerPC"; #else return "Unknown architecture"; #endif } static constexpr std::size_t ITERATIONS = 1000000; static constexpr std::size_t NUM_THREADS = 10; using result = std::pair<int, int>; std::vector<result> results; std::atomic_bool ready; std::atomic<int> x, y; void initialize() { results.clear(); results.resize(NUM_THREADS, {0, 0}); ready.store(false, std::memory_order_seq_cst); x.store(0, std::memory_order_seq_cst); y.store(0, std::memory_order_seq_cst); } void modify_x() { while (!ready.load(std::memory_order_seq_cst)) ; x.store(1, std::memory_order_seq_cst); } void modify_y() { while (!ready.load(std::memory_order_seq_cst)) ; y.store(1, std::memory_order_seq_cst); } void check(int i) { while (!ready.load(std::memory_order_seq_cst)) ; int xl = x.load(std::memory_order_seq_cst); int yl = y.load(std::memory_order_seq_cst); results[i] = {xl, yl}; } bool check_results() { // 统计四种结果的出现次数:{0,0}, {0,1}, {1,0}, {1,1} int arr[4] = {0, 0, 0, 0}; for (const auto &res : results) { if (res.first == 0 && res.second == 0) arr[0]++; else if (res.first == 0 && res.second == 1) arr[1]++; else if (res.first == 1 && res.second == 0) arr[2]++; else if (res.first == 1 && res.second == 1) arr[3]++; } bool failure = arr[1] != 0 && arr[2] != 0; if (failure) std::cout << "Results: {0, 0}: " << arr[0] << ", {0, 1}: " << arr[1] << ", {1, 0}: " << arr[2] << ", {1, 1}: " << arr[3] << " failure = " << failure << "\n"; return failure; } bool iterate(std::size_t iter) { initialize(); std::vector<std::jthread> threads_; threads_.reserve(NUM_THREADS + 2); for (std::size_t i = 0; i < NUM_THREADS; ++i) threads_.emplace_back(check, i); threads_.emplace_back(modify_y); threads_.emplace_back(modify_x); ready.store(1, std::memory_order_seq_cst); for (auto &t : threads_) t.join(); return check_results(); } int main() { std::cout << "Compiled on/for: " << machine() << "\n"; std::cout << "Testing sequential consistency semantics\n"; std::size_t total_failures = 0; for (std::size_t i = 0; i < ITERATIONS; ++i) total_failures += iterate(i); std::cout << "Total failed iterations: " << total_failures << " out of " << ITERATIONS << "\n"; return 0; }
编译运行命令及输出
我用以下命令编译并运行程序:
g++ rough2.cpp -std=c++20 -o prog && ./prog
输出结果(截取关键部分):
Compiled on/for: Compiled on/for: ARM64 (AArch64) Testing sequential consistency semantics Results: {0, 0}: 4, {0, 1}: 1, {1, 0}: 1, {1, 1}: 4 failure = 1 Results: {0, 0}: 4, {0, 1}: 1, {1, 0}: 1, {1, 1}: 4 failure = 1 Results: {0, 0}: 5, {0, 1}: 1, {1, 0}: 1, {1, 1}: 3 failure = 1 ... Total failed iterations: 15 out of 1000000
核心疑问
按我对std::memory_order_seq_cst全局单序的理解,所有seq_cst操作应该遵循一个统一的全局顺序,那为什么在ARM64平台上会出现这种交叉结果呢?是我对全局单序的理解有误,还是ARM64的硬件/编译器实现有特殊逻辑?亦或是我的测试代码里存在没注意到的漏洞?
麻烦各位大佬帮忙分析一下,谢谢啦!
内容来源于stack exchange

