我知道C ++标准并不保证数据竞争存在的任何事情(我相信数据竞争有未定义的行为,意味着任何事情,包括程序终止,修改随机内存等......)。 / p>
是否有任何架构,其中包含一个写入内存位置的线程和一个从同一位置读取的线程(没有同步)的数据争用不会导致读取操作读取未定义的值,并且内存位置为& #34;最终" (在内存屏障之后)更新为写操作写入的值?
[编辑以取代"竞争条件"与"数据竞赛"]
答案 0 :(得分:3)
数据竞争的问题不在于,您可以在计算机级别读取错误的值。 数据竞争的问题在于,编译器和处理器都对代码执行了大量优化。为了确保在存在多个线程时这些优化是正确的,它们需要有关可在线程之间共享的变量的其他信息。这种优化可以是例如:
Hans Boehm有一篇名为How to miscompile programs with "benign" data races的好文章良性数据竞赛。以下摘录摘自本文:
仔细检查延迟初始化
众所周知,这在源代码中是不正确的 水平。典型的用例类似于
if (!init_flag) { lock(); if (!init_flag) { my_data = ...; init_flag = true; } unlock(); } tmp = my_data;
没有什么能阻止优化编译器重新排序设置
my_data
与init_flag
的{{1}},或甚至提升my_data
的负载 在init_flag
的第一次测试之前,在条件init_flag
中重新加载它 没设定。即使编译器,某些非x86硬件也可以执行类似的重新排序 不执行任何转换。其中任何一个都可能导致my_data
的最终读取 看到未初始化的值并产生不正确的结果。
以下是另一个示例,其中int x
是共享的,int r
是局部变量。
int r = x;
if (r == 0)
printf("foo\n");
if (r != 0)
printf("bar\n");
如果我们只说,读数x
会导致未定义的值,那么程序将打印“foo”或“bar”。但是如果编译器按如下方式转换代码,程序也可能同时打印两个字符串。
if (x == 0)
printf("foo\n");
if (x != 0)
printf("bar\n");
答案 1 :(得分:0)
你可以使用Linux操作系统,你可以在c ++中通过父进程分叉2个或更多的子进程,你可以使两个访问一个内存位置,通过使用同步,你可以实现你想做的.--&gt ; How to share memory between process fork()? ,http://en.wikipedia.org/wiki/Dekker's_algorithm,http://en.wikipedia.org/wiki/Readers%E2%80%93writers_problem,
答案 2 :(得分:0)
总是会产生竞赛位置的一个示例:要求两个线程为相同的变量写入不同的值。 我们假设
即使使用互斥锁,您也会获得竞争条件,因为
线程的顺序取决于操作系统,并且没有关于首先使用哪个线程的保证。否则它将是顺序的,不需要在单独的线程中进行。
现在假设您根本没有同步,并且您在第二个线程中的第一个线程a = a + 2中执行a = a + 1。 a的初始值为0。
在汇编中,生成的代码是将a的值复制到一个寄存器中,向它添加1(在第一个线程的情况下,否则为2)。
如果完全没有同步,您可以按照以下顺序进行操作
Thread1:复制到reg1的值。 reg1包含0
Thread2:复制到reg2的值。 reg2包含0
线程1:增加了reg1的值1.现在包含1
线程2:添加了reg2的值2.现在包含2
线程1:增加了reg1的值1.现在包含1
线程2:添加了reg2的值2.现在包含2
Thread1:reg1的值放到a。现在包含1
Thread2:reg2的值放到a。现在包含2
如果你执行了thread1然后顺序线程2,你最后会得到= 3.
现在假设a是一个指针,即一个地址,如你所知,得到一个错误的指针地址会导致程序崩溃。因此,错误的同步会导致程序崩溃。
有道理吗?