C ++内存模型 - 此示例是否包含数据竞争?

时间:2014-01-01 17:11:53

标签: c++ c++11 memory-model

我正在阅读Bjarne Stroustrup的C ++ 11常见问题解答,但我无法理解memory model部分中的示例。

他提供了以下代码段:

// start with x==0 and y==0
if (x) y = 1; // thread 1
if (y) x = 1; // thread 2

常见问题解答说这里有的数据竞争。我不明白。内存位置x由线程1读取并由线程2写入而没有任何同步(y也是如此)。这是两次访问,其中一次是写入。这不是数据竞争的定义吗?

此外,它说“每个当前的C ++编译器(我所知道的)给出了正确答案。”这个正确答案是什么?根据一个线程的比较是在另一个线程的写入之前还是之后发生(或者另一个线程的写入对读取线程是否可见),答案是否会有所不同?

5 个答案:

答案 0 :(得分:14)

// start with x==0 and y==0
if (x) y = 1; // thread 1
if (y) x = 1; // thread 2

由于x和y都不为真,另一个也不会设置为真。无论执行指令的顺序如何,(正确的)结果总是x保持为0,y保持为0。

答案 1 :(得分:6)

  

内存位置x由线程2写入

真的吗?你为什么这么说?

如果y为0,则x 由线程2写入。y从0开始。同样,x不能除非在“线程1运行之前y非零”,否则不为零,这是不可能发生的。这里的一般观点是,不执行的条件写入不会导致数据竞争。

然而,这是内存模型的一个重要事实,因为将允许不知道线程的编译器(假设y不易变)将代码if (x) y = 1;转换为int tmp = y; y = 1; if (!x) y = tmp;。然后成为数据竞赛。我无法想象为什么它会想要进行那种确切的转换,但这并不重要,重点是非线程环境的优化器可以做违反线程内存模型的事情。因此,当Stroustrup说他所知道的每个编译器给出了正确的答案时(就在C ++ 11的线程模型下,就是这样),这是关于C ++ 11线程编译器准备就绪的一个非平凡的陈述。

if (x) y = 1的更真实的转换是y = x ? 1 : y;。我相信这个导致你的例子中的数据竞争,并且在赋值y = y的标准中没有特殊的处理方式,这使得对读取执行不安全的执行是安全的y在另一个帖子中。您可能会发现难以想象它不起作用的硬件,无论如何我可能是错的,这就是为什么我使用上面不同的例子,这不太现实但是有明显的数据竞争。

答案 2 :(得分:3)

必须有写入的总顺序,因为没有线程可以写入变量xy,直到某个其他线程首次写入1要么是变量。换句话说,您基本上有三种不同的场景:

  1. 线程1写入y,因为xif语句之前的某个点被写入,然后如果线程2稍后出现,则写入{{1} } x的相同值,并且不会更改它之前的1值。
  2. 线程2可以写入1,因为在x语句之前的某个时刻y已更改,然后线程if将写入1如果它后来出现y的相同值。
  3. 如果只有两个主题,那么1语句会被跳过,因为ifx仍为0。

答案 3 :(得分:2)

没有写入,所以没有比赛。 x和y都保持为零。

(这是在讨论幻象写入的问题。假设一个线程在检查条件之前推测性地写了这个,然后尝试纠正后面的事情。这会破坏另一个线程,所以不允许。)

答案 4 :(得分:-2)

内存模型设置代码和数据区域的可支持大小。在比较链接源代码之前,我们需要指定内存模型,即可以设置数据和代码的大小限制。