c++ - 不明白为什么在第 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=”<<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 循环的迭代。
所有这些看起来都相当直观:当然,写入值的操作发生在读取该值的操作之前!对于默认的原子操作,这确实是真的(这就是为什么这是默认的),但它确实需要说明:原子操作还有其他选项来满足排序要求,我很快就会谈到
我很不明白,为什么“所有这些看起来都相当直观:当然,写入值的操作发生在读取该值的操作之前!”,请帮助我理解这句话。
解决方案
data_ready
由于该标志,写入值 (3) 的操作发生在读取值 (2) 的操作之后。写入器线程在写入操作后启用标志 (4),而读取器线程有一个循环,在启用标志之前不会继续。
编译器将原子加载或存储视为内存栅栏。使用原子变量的默认选项,编译器不会重新排序跨此栅栏的操作。所以顺序总是 3 -> 4 -> (循环结束) -> 2。
使用其他选项,编译器可能会重新排序每个函数中的操作,以便在 3 之前执行 4,或者在 1 之前执行 2。
您可以将选项作为参数提供给load()
or store()
。请参阅文档。
推荐阅读
- flutter - Dart 中的地图索引
- rabbitmq - Pika SelectConnection 适配器“未解析的属性引用”
- go - 改变作为参数传递给 Go 中函数的切片
- android - 如何匹配来自editText的字符串和firebase数据库中的字符串并返回匹配或不匹配?
- android - How to wait/observe incoming data from server correctly with rxjava/retrofit
- maven - 无法解析类 org.jenkinsci.plugins.workflow.support.steps.build.DownstreamFailureCause
- c - 该程序在运行时给出“分段错误”,但是当我在调试器中完全一样时它工作得很好
- vue.js - Vuex:dispatch 是否返回一个承诺,以便您可以链接它们?
- python-3.x - 在 Windows\Linux CentOS for Python3 上安装 quickfix 模块
- c# - ASP.NET Core,Telerik 网格,不显示数据,但控制器操作返回数据