标准原子同步和非原子变量:不是过时的数据?

Posted

技术标签:

【中文标题】标准原子同步和非原子变量:不是过时的数据?【英文标题】:std atomic synchronisation and non atomic variable : not a stale data? 【发布时间】:2019-01-09 12:21:04 【问题描述】:

我知道这段代码是正确的(除了delete 没有完成):

#include <thread>
#include <atomic>
#include <cassert>
#include <string>

std::atomic<std::string*> ptr;
int data;

void producer()

    std::string* p  = new std::string("Hello");
    data = 42;
    ptr.store(p, std::memory_order_release);


void consumer()

    std::string* p2;
    while (!(p2 = ptr.load(std::memory_order_acquire)))
        ;
    assert(*p2 == "Hello"); // never fires
    assert(data == 42); // never fires


int main()

    std::thread t1(producer);
    std::thread t2(consumer);
    t1.join(); t2.join();

但是,我想知道为什么消费者线程中的数据不能是陈旧的数据。是不是因为acquire操作?

【问题讨论】:

【参考方案1】:

分配给datahappens-beforeptr.store调用。访问data 发生在调用之后(是的,通过与原子对象同步的方式)。因此,保证访问可以看到先前分配的值。

【讨论】:

编译器必须确保对变量的读取遵守由写入分配的最后一个值,该写入在读取之前发生。这是如何实现的,是一个实现细节;通常,它会发出特定于 CPU 的指令。 那么这能走多远呢? ptr 提供的屏障是否保护了对该函数中所有内容的每个分配? 简单来说:release => 刷新缓存。获取 => 使缓存无效。消费操作要复杂得多^^ @LightnessRacesinOrbit 这样想。 std::string 构造函数对对象的成员进行了一系列赋值。我假设您不会对 consumer 在获得 ptr 指针后看到 *ptr 的完全初始化状态感到惊讶。这与 data 的工作原理相同。 @AntoineMorrier FWIW,将获取-释放语义推理为 刷新缓存 将是有害的。这些原子操作必然具有运行时成本,这可能会导致误解。对于像x86这样具有强内存模型的CPU,大多数存储指令已经具有获取-释放语义,并且std::atomic + memory_order_release只是为了防止编译时重新排序,机器代码没有不同之处。如果您不想使用std::atomic,则使用带有虚拟asm("":::"memory") 的普通变量就足够了。

以上是关于标准原子同步和非原子变量:不是过时的数据?的主要内容,如果未能解决你的问题,请参考以下文章

第十五章 原子变量和非阻塞同步机制

Java并发编程实战 第15章 原子变量和非阻塞同步机制

混合原子和非原子变量和缓存

同步互斥阻塞

C++17 原子和条件变量死锁

非原子变量的障碍和同步点——数据竞争?