在简单情况下,同时写并阅读布尔值的危险
Dangers of simultaneous write and read of a boolean in a simple situation
我已经阅读了一些类似的问题,但是所描述的情况更复杂。
i在堆中有一个bool b
为false
,并且有两个线程。我确实知道使用bools
的操作是not atomic
,但请阅读该问题直到最后。
第一个线程只能设置b = true
一次,并且对此不做任何其他操作。第二个线程在循环中检查b
,如果是true
,请执行一些操作。
我是否需要使用一些同步机制(例如静音)来保护b
?如果我不这样做会发生什么?使用ints
,当我同时阅读和写入时,显然可以获得任意值。但是使用bools
只有true
和false
,我不介意一次获得false
而不是true
。这是潜在的SIGSEGV
?
数据种族导致不确定的行为。就标准而言,允许实施一个符合的实现。
实际上,主要危险是,如果没有同步,编译器将在读取器循环中观察到足够多的代码,以判断b
"永不更改",并优化除了第一个值之外的所有内容。它可以做到这一点,因为如果它观察到循环中没有同步,那么它知道任何写入值的写入都是数据竞赛。允许优化器假设您的程序不会引起不确定的行为,因此可以假设没有其他线程的写作。
将b
标记为volatile
将在实践中阻止这种特殊的优化,但是即使在volatile
上,数据种族也是不确定的行为。调用优化器"看不到"的代码也将在实践中阻止优化,因为它不知道该代码是否修改了b
。当然,通过链接时间/全程图优化,优化器看不到,而不是仅通过编译时间优化。
无论如何,防止在软件中进行优化并不能阻止与具有非连接缓存的系统上硬件中发生的等效事物(至少,我声称:其他人认为这是不正确的,并且volatile
访问需要通过缓存读/写入。某些实现的行为确实如此)。如果您询问标准所说的内容,那么硬件是否会无限期地显示出陈旧的缓存并不重要,因为行为仍然不确定,因此实现可能会破坏您的代码/em>优化是打破它的东西。
您可能获得的问题是,我们不知道读者线程看到更改值需要多长时间。如果它们在不同的CPU上,则没有单独的缓存,除非您使用内存屏障来同步缓存,否则无法保证。
在x86上,这是由硬件协议自动处理的,但在其他某些系统上不处理。
我需要使用一些同步机制(例如静音)来保护
b
?
如果不这样做,则有数据竞赛。具有数据竞赛的程序具有不确定的行为。这个问题的答案与"您希望您的程序具有明确的行为?"的答案相同。
如果我不这样做会发生什么?
理论上,任何事情都可能发生。这就是不确定的行为的含义。可能发生的最可能发生的坏事是"第二个线程"可能永远不会看到true
值。
编译器可以假定程序没有数据竞赛(如果其行为不是由标准定义的,那么行为就好像不正常了)。由于第二个线程仅从具有值false
的变量读取,并且没有影响这些读取的同步,因此逻辑上的结论是该值永远不会改变,因此循环是无限的。(并且某些无限环在C 11中具有不确定的行为!)
这是一些替代解决方案:
-
使用静音,在上面的其他答案中介绍了详细信息。
-
考虑使用读/写锁定,该锁可以管理/保护同时读取和写入。Pthread Lib提供了一个实现:PTHREAD_RWLOCK_T
-
取决于您的应用程序的操作,请考虑使用条件变量(Pthread Lib Ingl:pthread_cond_t)。这实际上是一个从一个线程到另一个线程的信号,这可以使您可以删除循环并进行检查。
使布尔挥发性足够(在x86体系结构上),无需静音:
volatile bool b;
- 在没有定义返回类型的函数中返回布尔值,并将结果保存在无错误的char编译中-为什么
- 变量定义到C++布尔值转换
- 如何确保在使用基于布尔值的两个方法之一调用方法时避免分支预测错误
- 重载更少,则运算符返回相反的布尔值
- 将此布尔值传递给此函数的最有效方法是什么?
- 如何设置 c++ 类的布尔值?
- 使用 MAKEWORD / MAKEWPARAM 使用布尔值而不是布尔值
- 将 10 个线程与原子布尔值同步
- 创建类似于布尔值的变量类型
- 布尔值向量的基于范围 for 循环
- 零点和布尔值之间的比较
- 简化对两个布尔值的 4 个 if/else 检查
- 无法创建带有布尔值和矢量的地图
- 对于完成布尔值设置为 true 后未停止的循环
- fstream / ifstream / ofstream 对象如何转换为布尔值
- C++:将值赋值给原始数据类型(例如布尔值)是原子操作吗?
- 为什么布尔值不能比作最后一点?
- 如何使用返回布尔值的函数?
- 在简单情况下,同时写并阅读布尔值的危险
- 从双精度到布尔值的 C++ 隐式转换是危险的