,c++,atomic,memory-model,happens-before"/>

首页 > 解决方案 > 不明白为什么在第 5.3.1 章中的“操作写入值发生在读取该值的操作之前”

问题描述

我目前在内存模型部分(第 5 章)中阅读了“C++ Concurrency in action”。在第 5.3.1 章中,作者写道:

#include <vector>
#include <atomic>
#include <iostream>

std::vector<int> data;
std::atomic<bool> data_ready(false);

void reader_thread()
{
    while(!data_ready.load())    // (1)
    {
        std::this_thread::sleep(std::milliseconds(1));
    }
    std::cout<<”The answer=”&lt;<data[0]<<”\n”;    // (2) 
}

void writer_thread()
{
    data.push_back(42);  // (3)
    data_ready=true;     // (4) 
}

抛开等待数据准备好的循环的低效率(1),你真的需要它来工作,因为否则在线程之间共享数据变得不切实际:每一项数据都被迫成为原子的。您已经了解到,非原子读取(2)和写入(3)在没有强制排序的情况下访问相同的数据是未定义的行为,因此要使其工作,必须在某处强制排序。

所需的强制排序来自对 std:: 原子变量 data_ready 的操作;它们通过发生在之前和同步的内存模型关系提供必要的排序。数据的写入(3)发生在数据就绪标志(4)的写入之前,标志(1)的读取发生在数据的读取(2)之前。当从 data_ready (1)读取的值为真时,写入与读取同步,创建发生前的关系。因为happens-before是传递性的,所以对数据的写入(3)发生在写入标志(4)之前,发生在从标志(1)读取真值之前,发生在读取之前数据(2),你有一个强制的顺序:数据的写入发生在数据的读取之前,一切正常。图 5。图 2 显示了两个线程中的重要发生之前的关系。我从阅读器线程中添加了几次 while 循环的迭代。

所有这些看起来都相当直观:当然,写入值的操作发生在读取该值的操作之前!对于默认的原子操作,这确实是真的(这就是为什么这是默认的),但它确实需要说明:原子操作还有其他选项来满足排序要求,我很快就会谈到

我很不明白,为什么“所有这些看起来都相当直观:当然,写入值的操作发生在读取该值的操作之前!”,请帮助我理解这句话。

标签: c++atomicmemory-modelhappens-before

解决方案


data_ready由于该标志,写入值 (3) 的操作发生在读取值 (2) 的操作之后。写入器线程在写入操作后启用标志 (4),而读取器线程有一个循环,在启用标志之前不会继续。

编译器将原子加载或存储视为内存栅栏。使用原子变量的默认选项,编译器不会重新排序跨此栅栏的操作。所以顺序总是 3 -> 4 -> (循环结束) -> 2。

使用其他选项,编译器可能会重新排序每个函数中的操作,以便在 3 之前执行 4,或者在 1 之前执行 2。

您可以将选项作为参数提供给load()or store()请参阅文档。


推荐阅读