c++条件语句是否带有从条件表达式到语句的依赖关系?

Do C++ conditional statements carry a dependency from the condition expression to the statement?

本文关键字:语句 条件 依赖 关系 是否 c++ 表达式      更新时间:2023-10-16

我特别从内存模型的角度提出这个问题。http://en.cppreference.com/w/cpp/atomic/memory_order

我问是因为我想知道我是否可以在下面使用std::memory_order_consume:

mLocalMemPtr1和2和mAtomicMemPtr是指向共享缓冲区的指针。

在一个producer线程中,我正在做:

for (int x = 0; x < 10; ++x)
{
    ++mLocalMemPtr1
    *mLocalMemPtr1 = x;       // <========= A
    mAtomicMemPtr.store(mLocalMemPtr1, std::memory_order_release);
}

在消费者中:

tempMemPtr = mAtomicMemPtr.load(std::memory_order_consume);
while (tempMemPtr != mLocalMemPtr2)
{
    ++mLocalMemPtr2;
    int test = *mLocalMemPtr2;   // <======== B
    doSomeLongRunningThing(test);
    tempMemPtr = mAtomicMemPtr.load(std::memory_order_consume);
}

所以依赖链去tempMemPtr -> mLocalMemPtr2 ->测试-> doSomeLongRunningThing?

我特别担心B可能在A之前执行。我知道我可以使用std::memory_order_acquire,但是如果条件语句导致内存顺序依赖,我可以使用consume(这是更轻量级的)。

CppReference:

Release-Consume订购

如果线程A中的原子存储标记为std::memory_order_release,线程B中来自同一变量的原子加载标记为std::memory_order_consume,那么从线程A的角度来看,所有依赖顺序在原子存储之前的内存写入(非原子和宽松原子)在线程B中都成为可见的副作用,也就是说,一旦原子加载完成,如果线程B 在原子加载中携带了数据依赖项,则线程B保证看到线程A写入内存的所有内容。

1.10.10:

如果

,则求值A在求值B之前是依赖顺序的

- A对原子对象M执行释放操作,在另一个线程中,B对M执行消费操作,读取由A(…)开头的释放序列中的任何副作用写的值

1.10.9:

如果A的值被用作B的操作数,则计算值A携带对计算值B的依赖,除非:

- B是std::kill_dependency(29.3)的任何特化的调用,或者

—A是内置逻辑与(&&,见5.14)或逻辑或(||,见5.15)操作符的左操作数,或

—A是条件(?:,参见5.16)操作符,或

—A是内置逗号(,)操作符(5.18)的左操作数;(…)

基于这些事实,我说mLocalMemPtr2应该同步。然而,仍然存在一个求值顺序的问题。
if (atomic.load(std::consume) < x)

先计算哪一个是未指定的。不保证(因为我在标准中找不到它)编译器将首先执行消费操作,刷新共享缓冲区,然后加载atomic ,然后加载 x

没有找到在"希望"中求值的操作数的证明;如果没有显式分解原子负载mLocalMemPtr2,它将无法工作,CPU可能会读取由mLocalMemPtr2指向的内存的陈旧值。memory_order_acquire在这里不会改变太多,因为mLocalMemPtr2携带了一个数据依赖项。

我相信通过consume排序,编译器实际上可以提前复制整个mSharedBuffer。您需要acquire语义来使以前缓存的除mAtomicMemLocPtr以外的变量副本无效.

如果从字面上理解它们在carry依赖中所陈述的内容。那么我会说

tempMemPtr = mAtomicMemPtr.load(std::memory_order_consume);

将依赖项带入循环的条件(将其视为布尔变量标志)。但是,在任何循环体操作中,该条件都不会作为操作数被读取(也不会写入由循环体中的另一个操作读取的对象)。所以我们需要一个获取操作来让在释放之前被排序的操作也发生在获取之后的操作之前(读和写释放)而不依赖于消费操作和循环体中操作之间的数据依赖关系

可能包含发布消费语义的解决方案是:

tempMemPtr = mAtomicMemPtr.load(std::memory_order_consume);
while (tempMemPtr != mLocalMemPtr2)
{
    mLocalMemPtr2 = tempMemPtr; // that line adds the dependency needed 
    ++mLocalMemPtr2;
    int test = *mLocalMemPtr2;
    doSomeLongRunningThing(test);
    tempMemPtr = mAtomicMemPtr.load(std::memory_order_consume);
}