我正在阅读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 ++编译器(我所知道的)给出了正确答案。”这个正确答案是什么?根据一个线程的比较是在另一个线程的写入之前还是之后发生(或者另一个线程的写入对读取线程是否可见),答案是否会有所不同?
答案 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)
必须有写入的总顺序,因为没有线程可以写入变量x
或y
,直到某个其他线程首次写入1
要么是变量。换句话说,您基本上有三种不同的场景:
y
,因为x
在if
语句之前的某个点被写入,然后如果线程2稍后出现,则写入{{1} } x
的相同值,并且不会更改它之前的1
值。1
,因为在x
语句之前的某个时刻y
已更改,然后线程if
将写入1
如果它后来出现y
的相同值。1
语句会被跳过,因为if
和x
仍为0。答案 3 :(得分:2)
没有写入,所以没有比赛。 x和y都保持为零。
(这是在讨论幻象写入的问题。假设一个线程在检查条件之前推测性地写了这个,然后尝试纠正后面的事情。这会破坏另一个线程,所以不允许。)
答案 4 :(得分:-2)
内存模型设置代码和数据区域的可支持大小。在比较链接源代码之前,我们需要指定内存模型,即可以设置数据和代码的大小限制。