【问题标题】:Visible sequence of side effects可见的副作用序列
【发布时间】:2019-09-07 20:37:27
【问题描述】:

我对@9​​87654322@中定义的可见的副作用序列概念的定义感到困惑:

原子对象M 上的可见副作用序列,带有 关于M 的值计算B,是最大连续的 M 的修改顺序中的副作用子序列,其中 对于B,第一个副作用是可见的,并且对于每个 后续的副作用,不是B发生在它之前。

这是我目前对该概念的理解:

由于关于 B 的可见副作用应该是“在B 之前发生的最接近的副作用”,我可以想象以下情况:

_Atomic int i = 10;

void *increment(void *ignored){
    i++;
    printf("%d\n", i); //<<----- B
}

void *decrement(void *ignored){
    i--;
    printf("%d\n", i);
}

int main(void){
   i = 100;
   //start two threads with increment and decrement correspondingly
}

考虑到上述情况,我假设Bincrement 函数中的值计算,因此关于B 的可见副作用序列是“i++, i--”。由于i = 100 是在创建线程之前排序的,因此它不属于B 的可见副作用序列。

我的解释是真的吗?

【问题讨论】:

  • 您混淆了 C 和 C++。 C++ 不是 C。委员会将这个概念从标准中删除,因为它是多余的。 open-std.org/jtc1/sc22/wg14/www/docs/dr_406.htm
  • @alinsoar 你混淆了 C 和 C++ 我不太明白你的意思。我什至没有阅读 C++ 对它的规定。另外你为什么删除C标签?我引用了 N1570 草案。我把它回滚了。
  • 过失。我以为它是从 C++ 引用的。但是您可以在我发布的技术勘误中找到答案。
  • 我认为您在这里做出了错误的假设,即如果您依次启动 2 个线程“A”然后“B”,那么线程“A”将在线程“B”之前运行。他们将可能按该顺序执行,但不能保证。

标签: c multithreading concurrency language-lawyer


【解决方案1】:

C2x 标准的working draft 包含了在 C17(“错误修正”)标准中所做的更改。本节更改的主要目的是调整 C++ 和 C 之间的内存一致性模型,并删除 DR406 缺陷报告中指出的冗余。

如您所见,委员会不遗余力地尝试澄清内存一致性模型并简化“数据竞赛”的构成要素。

棘手的部分是 C17 错误修正中只有一项更改是“规范的”,这意味着在 C2x 标准最终确定之前,符合要求的实现不必考虑对本节的更改。

就您对当前标准的现有部分的解释而言,我不得不说这是一个有争议的问题。标准委员会承认当前版本中的混乱和模棱两可,您最好等到这部分有机会在新的 C2x 标准下安定下来,然后再过分担心。

16 如果 A 与 B 同步,则评估 A 线程间发生在评估 B 之前,A 是 B 之前的依赖排序,或者,对于某些评估 X:

  • A 与 X 同步,X 在 B 之前排序,
  • A 在 X 之前排序,并且 X 线程间发生在 B 之前,或者
  • 线程间发生在 X 之前,而 X 线程间发生在 B 之前。


    15)“携带依赖”关系是“sequenced before”关系的子集,同样严格地在线程内。
    16) “dependency-ordered before”关系类似于“synchronizes with”关系,但在 发布/获取的地方。

17 注 7 “线程间发生之前”关系描述了“sequenced before”、“synchronizes”的任意连接 with”和“dependency-ordered before”关系,但有两个例外。第一个例外是串联是 不允许以“dependency-ordered before”后跟“sequenced before”结尾。这个限制的原因是 参与“依赖排序之前”关系的消费操作仅提供相对于的排序 此消费操作实际上带有依赖关系的操作。此限制仅适用的原因 在这种连接的最后,任何后续的发布操作都将为之前的发布操作提供所需的排序 消费操作。第二个例外是不允许串联完全由“sequenced before”组成。 这种限制的原因是(1)允许“线程间发生之前”被传递关闭和(2)“发生 before”关系,定义如下,提供完全由“sequenced before”组成的关系。

18 如果 A 在 B 之前排序或 A 线程间发生,则评估 A 发生在评估 B 之前 之前 B. 实施应确保没有程序执行演示循环 “发生在”关系之前。

19 注意 8 否则此循环只能通过使用消耗操作来实现。

20 相对于 M 的值计算 B,对象 M 上的可见副作用 A 满足 条件:

  • A 发生在 B 之前,并且
  • X 对 M 没有其他副作用,即 A 发生在 X 之前,X 发生在 B 之前。 由评估 B 确定的非原子标量对象 M 的值应为存储的值 由可见的副作用 A.

21 注 9 如果对非原子对象的哪个副作用可见存在歧义,则存在数据竞争,并且 行为未定义。

22 注意 10 这表明对普通变量的操作没有明显的重新排序。这实际上是无法检测到的 数据竞争,但有必要确保数据竞争,如这里定义的那样,并且对原子的使用有适当的限制, 对应于简单交错(顺序一致)执行中的数据竞争。

23 由评估 B 确定的原子对象 M 的值应是某个存储的值 修改 M 的副作用 A,其中 B 不会在 A 之前发生。

24 注 11 给定评估可能取其值的副作用集也受到其余规则的限制 此处进行了描述,特别是通过以下一致性要求进行了描述。

25 如果修改原子对象 M 的操作 A 发生在修改 M 的操作 B 之前, 那么A在M的修改顺序上要早于B。

26 注 12 上述要求称为“写-写一致性”。

27 如果原子对象 M 的值计算 A 发生在 M 的值计算 B 之前,并且 A 从 M 上的副作用 X 中获取其值,则 B 计算的值应为该值 由 X 存储或由 M 上的副作用 Y 存储的值,其中 Y 在修改中跟随 X M 阶。

28 注 13 上述要求称为“读取-读取一致性”。

29 如果原子对象 M 的值计算 A 发生在对 M 的操作 B 之前,则 A 应 从 M 上的副作用 X 中获取其值,其中 X 在 M 的修改顺序中位于 B 之前。

30 注 14 上述要求称为“读写一致性”。

31 如果原子对象 M 上的副作用 X 发生在 M 的值计算 B 之前,则 评估 B 应从 X 或修改中 X 之后的副作用 Y 获取其值 M 阶。

32 注 15 上述要求称为“读写一致性”。

33 注意 16 这实际上禁止编译器将原子操作重新排序到单个对象,即使这两个操作都是 “放松”的负载。通过这样做,它有效地使大多数硬件提供的“缓存一致性”保证可用于 C 原子操作。

34 注意 17 原子对象的负载观察到的值取决于“发生在之前”关系,而后者又取决于 关于由大量原子对象观察到的值。预期的阅读是存在原子负载的关联 with modifications they observe that, together with suitably chosen modification orders and the "happens before" relation 如上所述派生,满足此处施加的结果约束。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2015-09-15
    • 1970-01-01
    • 2013-07-02
    • 1970-01-01
    • 1970-01-01
    • 2013-11-18
    • 1970-01-01
    相关资源
    最近更新 更多