在 C++ 中将值从 X 更改为 X 会导致数据竞争吗?

Can changing a value from X to X in C++ lead to a data race?

我的代码适用于具有不同布局的大型数据块。布局会决定哪些数据是固定的,哪些数据是不固定的。一旦数据固定在块中,它通常不会再更改。所以所有的代码读取数据总是会看到相同的数据。

但是,其他服务可能会更改这些块,只要它们确定没有代码会读取块的那部分。为了简化代码,包含更改的块将从一项服务发送到另一项服务,而不管块的布局如何。然后接收服务将覆盖整个块,包括未更改的数据。让我用一个例子来说明这一点:

假设我们有以下数据块:

57 23 98 17 25 00 00 00 00 00

假设前 5 个值是 'fixed'。我们服务中的代码只会读取前 5 个值,永远不会读取接下来的 5 个值。由于我们的架构设计,我们可以保证这一点。接下来的 5 个值实际上没有意义,所以我在 table 中放了零来说明这一点。

现在另一个服务确定接下来的 5 个值,将完整的块发送到我们的服务,我们只需用新数据覆盖整个块。由于前 5 个值是 'fixed',它们保持不变,但是传输和覆盖块的代码不知道块的布局,所以它唯一能做的就是覆盖整个块.这是结果:

57 23 98 17 25 08 33 42 71 85

如前所述,前 5 个值没有改变,尽管它们已被传输逻辑覆盖。

问题是:这是一场数据竞赛吗?如果其他线程可以同时读取数据,是否允许用完全相同的值覆盖内存地址?

“看情况”太多了,这个问题无法用明确的陈述来回答。

如果此构造是某些生产代码的一部分,那么答案是:肯定是。可能会出现竞争条件。必须使用任务同步机制。无法讨论。

Is this a data race?

是的。

Is it allowed to overwrite a memory address with exactly the same value if other threads can read the data at the same time?

不明确 - 这也不是唯一的问题。

如果您的编译器实际上执行了一个 8 字节的加载,那么您在最后 3 个字节上有一个 真实的(即,甚至可能不仅仅是理论上的)数据竞争。假设您有一台假设的机器,其中 uint64_t57 23 98 17 25 00 00 42 是一个陷阱表示,您的更新线程使用 memmove,并且它向后 复制更新 .

但是,数据竞争意味着行为未被标准定义。它可能在特定平台上定义明确——例如任何没有整数陷阱表示的平台,任何您知道编译器将真正使用字节加载的平台,或任何具有用于幂等存储的显式语义的平台。

参见,例如,[intro.races] note 23

Transformations that introduce a speculative read of a potentially shared memory location might not preserve the semantics of the C++ program as defined in this document, since they potentially introduce a data race. However, they are typically valid in the context of an optimizing compiler that targets a specific machine with well-defined semantics for data races. They would be invalid for a hypothetical machine that is not tolerant of races or provides hardware race detection

(对于非投机性竞赛没有这样的说明,对于幂等存储也没有特定的例外,但在 IMO 中采用相同的方法是合理的)。

显然,如果您可以编写代码,使其 依赖于这些平台细节,那么面对编译器 and/or平台更新。只是在块的两个版本之间进行原子交换(因此您从保证不变的副本中读取,并写入保证不共享的副本)将始终是正确的,并且由于减少 cache/coherency 甚至可能更快交通.