C++无锁原子变量atomic的compare_exchange_strong问题

今天在维护之前的无锁容器的时候出现了一个很奇怪的bug,在C++20时可以正常使用,在C++17时就会出现异常,最终debug把问题定位并缩小在atomic的compare_exchange_strong中,但我发现了一件非常恐怖的事情,仿佛是我写代码经历了量子危机。
有以下代码:

#include <atomic>
#include<iostream>
using namespace std;
struct MyStruct {
    int a;
    double b;
};
int main() {
    std::atomic<MyStruct> myAtomic{ MyStruct{1, 2.0} };
    MyStruct expected{ 1, 2.0 };
    MyStruct desired{ 2, 3.0 };
    bool ok=myAtomic.compare_exchange_strong(expected, desired);
    cout << ok << endl;
    return 0;
}

运行上面一段代码,在肉眼观察下我们应该会推测应该会输出1,因为原子变量和预期值相等,交互应该会成功,但是奇怪的一幕出现了,我的Visual Studio C++17编译会输出0。嗯,我开始困惑,把编译器调回C++20,又正常输出1了。
后面我又把编译器调回了C++17 尝试找出问题所在,后面更恐怖的事情发生了,当我在compare_exchange_strong后面添加一句

    cout << myAtomic.load().a << " " << myAtomic.load().b << endl;

恐怖的事情发生了,这个结果输出了2 3 \n 1 对,ok又变成了true,我甚至一度怀疑我眼花了,把这句又删除了,输出又变成了0,对ok又变成了false,难道我对这个原子变量是否进行观测还会还会影响compare_exchange_strong的执行?我一度又检测了代码的内存次序,强制调整成std::memory_order_seq_cst,问题依然如此。
好的,我甚至开始怀疑是不是编译时内存次序还有什么隐秘的问题,我把上面的观测代码放到输出ok后面,输出依然是1 \n 2 3 对,ok又变成true了,仿佛是量子力学的观察者效应。希望强者给我一些解答,这是否是VS C++17标准库的缺陷还是我的代码出现了什么问题?

内存对齐一下

struct MyStruct
{
    int64_t a;
    double b;
};


以下答案由GPT-4.5Plus大模型(语音)与博主A努力中.......共同编写:
atomic::compare_exchange_strong是C++中实现无锁原子变量的重要函数。它的作用是:

如果期望值(第一个参数)和内存中的值相同,则将内存的值设为新值(第三个参数),并返回true。
如果期望值和内存中的值不同,则将内存中的值赋值给期望值(第一个参数),并返回false。

简单示例:

cpp
std::atomic<int> a{0};
int expect = 0;
bool b = a.compare_exchange_strong(expect, 5);  // b为true,a变为5,expect保持0 
expect = 0;
b = a.compare_exchange_strong(expect, 10); // b为false,expect变为5,a保持5
第一次比较期望值为0和a的值0相同,所以a成功设为5,b返回true。
第二次比较期望值为0和a的值5不同,所以expect被赋值为a当前的值5,b返回false。
compare_exchange_strong与compare_exchange_weak的区别在于:
- compare_exchange_strong是强比较,需要内存的值和期望值完全相同才会执行交换操作。
- compare_exchange_weak是弱比较,只要内存的值符合期望值,就会执行交换操作。

弱比较在某些架构上能够提供略微的性能提升,但是代价是结果存在一定的随机性。所以除非是追求极限性能,一般建议使用强比较的compare_exchange_strong。