C++memory_order_sume,kill_dependency,dependency在之前排序,与同步

C++ memory_order_consume, kill_dependency, dependency-ordered-before, synchronizes-with

本文关键字:dependency 排序 同步 order kill C++memory sume      更新时间:2023-10-16

我正在阅读Anthony Williams的《C++并发在行动》。目前,我正处于他描述memory_order_sume的位置。

在该块之后有:

既然我已经介绍了内存排序的基本知识,现在是时候看看更复杂的零件

这让我有点害怕,因为我不完全理解以下几点:


之前排序的依赖项与同步的依赖项有何不同?他们都创造了发生在关系之前的事。到底有什么不同?


我对以下例子感到困惑:

int global_data[]={ … };
std::atomic<int> index;
void f()
{
    int i=index.load(std::memory_order_consume);
    do_something_with(global_data[std::kill_dependency(i)]);
}

kill_dependency到底做什么?它杀死了哪个依赖?在哪些实体之间?编译器如何利用这些知识?


是否可以用memory_order_acquire安全地替换所有出现的memory_order_sume?也就是说,它在所有意义上都更严格吗?


在清单5.9中,我可以安全地替换吗

std::atomic<int> data[5]; // all accesses are relaxed

带有

int data[5]

即获取和释放可以用于同步访问非原子数据吗?


他通过一些隔间里男人的例子描述了放松、获得和释放。seq_cst和consume是否有类似的简单描述?

关于倒数第二个问题,答案需要更多的解释。当多个线程访问相同的数据时,有三件事可能会出错:

  1. 系统可能会在读取或写入过程中切换线程,从而产生一半是一个值,一半是另一个值的结果。

  2. 编译器可能会移动代码,前提是没有其他线程查看所涉及的数据。

  3. 处理器可以在其本地高速缓存中保持一个值,而不在改变该值之后更新主存储器或者在另一个线程改变主存储器中的值之后重新读取该值。

内存顺序地址数字3。原子函数寻址1和2,根据内存顺序参数的不同,也可能寻址3。所以memory_order_relaxed的意思是"不要为数字3而烦恼。代码仍然处理1和2。在这种情况下,您可以使用获取和释放来确保正确的内存顺序。

之前排序的依赖项与同步的依赖项有何不同?

从1.10/10开始:"[注:关系"在之前是依赖排序的"类似于"与同步",但使用发布/消费代替发布/获取。--结束注释]"。

kill_dependency到底做什么?

一些编译器进行数据依赖性分析。也就是说,它们跟踪变量中值的变化,以便更好地了解需要同步的内容。kill_dependency告诉此类编译器不要进一步跟踪,因为代码中存在编译器无法理解的内容。

是否可以安全地将所有出现的memory_order_sume替换为memory_order_acquire?也就是说,它在所有意义上都更严格吗?

我想是的,但我不确定。

memory_order_sume要求原子操作发生在所有依赖于它的非原子操作之前。数据依赖关系是指在不使用该数据的情况下无法计算表达式的任何依赖关系。例如,在x->y中,如果不首先评估x,就无法评估x->y。

kill_dependency是一个惟一的函数。所有其他函数的参数都有数据依赖关系。Kill_dependency明确没有。当您知道数据本身已经同步,但您需要获取的数据表达式可能没有同步时,它就会显示出来。在您的示例中,允许do_something_with假设globalldata[i]的任何缓存值都可以安全使用,但i本身实际上必须是正确的原子值。

如果所有对数据的更改都通过匹配的memory_order_release正确释放,那么memory_orderAcquire严格来说更强。