C++内存模型——这个例子包含数据竞赛吗

C++ memory model - does this example contain a data race?

本文关键字:数据 包含 竞赛 内存 模型 C++      更新时间:2023-10-16

我正在阅读Bjarne Stroustrup的C++11常见问题解答,我很难理解内存模型部分的一个例子。

他给出了以下代码片段:

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

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

此外,它还说"(据我所知)每一个当前的C++编译器都给出了一个正确的答案。"这个正确的答案是什么?答案难道不会因为一个线程的比较发生在另一个线程写入之前还是之后而有所不同吗?

// 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。

内存位置x是。。。由线程2 写入

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

如果y为0,则x不是由线程2写入的。并且CCD_ 6从0开始。类似地,除非y在线程1运行之前是非零的,否则x不可能是非零,而这是不可能发生的。这里的要点是,不执行的条件写入不会导致数据竞争。

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

CCD_ 12的一个更现实的变换将是CCD_。我认为,在您的示例中,这个将导致数据争用,并且在分配y = y的标准中没有特殊处理,因此可以安全地执行相对于另一个线程中的y的读取未排序的操作。你可能会发现很难想象它在硬件上不起作用,无论如何,我可能错了,这就是为什么我使用了上面的另一个例子,这个例子不太现实,但有明显的数据竞争。

必须有一个写入的总顺序,因为在其他线程首次将1写入任一变量之前,没有线程可以写入变量xy。换句话说,你基本上有三种不同的场景:

  1. 线程1可以写入y,因为x是在if语句之前的某个前一点写入的,然后如果线程2稍后到来,它会向x写入与1相同的值,并且不会更改其先前的1
  2. 线程2可以写入x,因为yif语句之前的某个时间点发生了更改,然后线程1将写入y(如果稍后1的值相同)
  3. 如果只有两个线程,那么if语句将被跳过,因为xy保持为0

这两次写入都没有发生,因此没有竞争。x和y都保持为零。

(这是关于虚幻写入的问题。假设一个线程在检查条件之前进行了推测性的写入,然后试图在检查条件之后进行更正。这会破坏另一个线程,所以这是不允许的。)

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