c++中良性竞争条件的保证
Guarantees of benign race conditions in C++
我知道c++标准不保证存在数据竞争(我相信数据竞争有未定义的行为,意味着任何事情都会发生,包括程序终止,修改随机内存等)。
是否有任何架构,其中的数据竞争,包括一个线程写入内存位置和一个线程从同一位置读取(没有同步)不导致读取操作读取一个未定义的值,其中的内存位置是"最终"(内存屏障后)更新到由写操作写入的值?
数据竞争的问题不在于您可以在机器级别读取错误的值。数据竞争的问题是,编译器和处理器都对代码执行大量优化。为了确保这些优化在存在多个线程的情况下是正确的,它们需要关于可以在线程之间共享的变量的额外信息。这样的优化可以例如:
- 重新排序操作
- 添加额外的加载和存储操作
- 删除加载和存储操作
Hans Boehm有一篇很好的论文良性数据竞争,叫做如何用"良性"数据竞争错编译程序。以下节选自本文:
延迟初始化的双重检查
众所周知,这在源代码中是不正确的的水平。一个典型的用例看起来像
if (!init_flag) { lock(); if (!init_flag) { my_data = ...; init_flag = true; } unlock(); } tmp = my_data;
没有什么可以阻止优化编译器重新排序
my_data
与init_flag
的负载,甚至my_data
的负载提前在init_flag
的第一次测试之前,在init_flag
条件下重新加载它没有设置。一些非x86硬件可以执行类似的重新排序,即使编译器不执行任何转换。这两种方法都可能导致my_data
的最终读取查看未初始化的值并产生不正确的结果。
下面是另一个例子,int x
是一个共享变量,int r
是一个局部变量。
int r = x;
if (r == 0)
printf("foon");
if (r != 0)
printf("barn");
如果我们只说,读取x
导致一个未定义的值,那么程序将输出"foo"或"bar"。但是,如果编译器按如下方式转换代码,程序也可能同时打印两个字符串,或者不打印任何字符串。
if (x == 0)
printf("foon");
if (x != 0)
printf("barn");
你可以使用linux操作系统,在c++中,你可以在父进程上分支2个或更多的子进程,你可以让两个进程访问一个内存位置,通过使用同步,你可以实现你想要做的事情。如何在进程fork()之间共享内存?, http://en.wikipedia.org/wiki/Dekker's_algorithm, http://en.wikipedia.org/wiki/Readers%E2%80%93writers_problem,
总是导致竞争位置的一个例子:要求两个线程向相同的变量写入不同的值。让我们假设
- 线程1设置变量a为1
- 线程2设置变量a为2
你会得到竞争条件,即使有互斥锁,因为
- 如果线程1先执行,那么你得到a=1然后a=2。
- 如果线程2先执行,那么你得到a=2然后a=1。
线程的顺序取决于操作系统,不能保证哪个线程会先执行。否则,它将是顺序的,不需要在单独的线程中执行。
假设现在根本没有同步,并且在第一个线程中执行a=a+1,在第二个线程中执行a=a+2。a的初值为0
在汇编中,生成的代码是将a的值复制到一个寄存器中,加1(对于第一个线程,加2)。
如果根本没有同步,则可以有以下顺序,例如
-
Thread1:拷贝到reg1的值。reg1包含0
-
Thread2:一个拷贝到reg2的值。reg2包含0
-
Thread1: reg1的值增加1。现在包含1
-
Thread2: reg2添加的值现在包含2
-
Thread1: reg1的值增加1。现在包含1
-
Thread2: reg2添加的值现在包含2
-
Thread1: reg1的值放到a.现在a包含1
-
Thread2: reg2的值放到a.现在a包含2
如果你已经执行了thread1,那么顺序执行线程2,你将在最后得到a=3。
现在假设a是一个指针,也就是一个地址,所以你知道,得到一个错误的指针地址可能会导致程序崩溃。所以错误的同步会导致程序崩溃。
有道理吗?
- compare_exchange C++函数如何确定竞争条件?
- 在C++中写入相同值的竞争条件?
- QByteArray 通过队列连接按值发出并连接并附加到竞争条件?
- 从stdin读取时子进程挂起(fork/dup2竞争条件)
- POSIX 条件变量和互斥体"竞争"
- 如何修复条件变量等待/通知的竞争条件
- 在没有互斥锁的情况下重新计数时如何避免竞争条件?
- 替代 rand() 以避免竞争条件?
- CUDA 内核中的竞争条件
- 为什么跨线程更改共享变量的代码显然没有受到竞争条件的影响
- 类声明自己(*this)为private以避免竞争条件/放弃在gcc中对threadprivate的请求
- 使用 gtest EXPECT_CALL 时竞争条件段错误,而另一个期望是执行相同的方法
- boost::进程间消息队列创建时的竞争条件
- 如何在没有竞争条件的情况下将 QFutureWatcher 与 QtConcurrent::run() 一起使用
- 为什么 printf 可以屏蔽竞争条件,而系统日志不能?
- 为什么这个代码会产生竞争条件
- 为什么 CUDA 同步点不能阻止竞争条件?
- 竞争条件:一个线程创建静态对象,另一个线程在初始化完成之前使用它.如何处理
- 如何使用 Boost Atomic 删除竞争条件
- boost::mutex 无法帮助避免C++程序中的竞争条件