在简单的情况下同时写入和读取布尔值的危险

时间:2012-10-30 08:52:14

标签: c++ multithreading nonatomic

我已经阅读了一些类似的问题,但所描述的情况有点复杂。

我在堆中初始化为bool b并且有两个线程。我确实理解false的操作是bools,但请将问题直到最后才能理解。

第一个帖子只能设置not atomic一次,不会对它做任何其他事情。 第二个线程在循环中检查b = true,如果b执行某些操作。

我是否需要使用某些同步机制(如互斥锁)来保护true? 如果我不这样做会怎么样?使用b当我在同一时间读写时,我显然可以得到任意值。但是ints只有boolstrue,我不介意一次获得false而不是false。它是潜在的true吗?

5 个答案:

答案 0 :(得分:7)

数据争用导致未定义的行为。就标准而言,允许符合规定的实施方式进行分段。

在实践中,主要的危险是如果没有同步,编译器将在读取器循环中观察足够的代码来判断b“永不改变”,并优化除了第一次读取值之外的所有内容。它可以这样做,因为如果它观察到循环中没有同步,那么它知道对值的任何写入都是数据竞争。允许优化器假设您的程序不会引发未定义的行为,因此允许假设没有来自其他线程的写入。

b标记为volatile会在实践中阻止此特定优化,但即使在volatile个对象上,数据争用也是未定义的行为。调用优化器“看不到”的代码也会阻止实际优化,因为它不知道该代码是否修改了b。当然,对于链接时/整个程序的优化,优化器看不到比仅编译时优化更少。

无论如何,防止在软件中进行优化并不能阻止在具有非连贯缓存的系统上硬件中发生的同等事情(至少,所以我声称:其他人认为这是不正确的,并且需要volatile次访问才能通过缓存进行读/写。有些实现的行为就是这样。如果您询问标准所说的内容,那么硬件是否无限期地向您显示过时缓存并不重要,因为行为仍然未定义,因此无论此特定<是否,实现都会破坏您的代码/ em>优化是破坏它的东西。

答案 1 :(得分:3)

可能获得的问题是我们不知道读者线程看到更改后的值需要多长时间。如果它们位于具有单独高速缓存的不同CPU上,则除非使用内存屏障来同步高速缓存,否则无法保证。

在x86上,这由硬件协议自动处理,但在其他一些系统上则不会。

答案 2 :(得分:2)

  

我是否需要使用某些同步机制(如互斥锁)来保护b

如果不这样做,那么您就有数据竞争。具有数据争用的程序具有未定义的行为。这个问题的答案与“你希望你的程序有明确的行为吗?”这个问题的答案相同。

  

如果我不这样做会怎么样?

理论上,任何事情都可能发生。这就是未定义的行为意味着什么。最可能发生的坏事是“第二个线程”可能永远不会看到true值。

编译器可以假设一个程序没有数据竞争(如果它的行为没有被标准定义,那么表现就好像它没有那么好)。由于第二个线程只读取具有值false的变量,并且没有影响这些读取的同步,因此逻辑结论是值永远不会改变,因此循环是无限的。 (有些无限循环在C ++ 11中有未定义的行为!)

答案 3 :(得分:0)

以下是一些替代解决方案:

  1. 使用互斥锁,详情已在上述其他答案中介绍。

  2. 考虑使用读/写锁来管理/保护同时读写。 pthread lib提供了一个实现:pthread_rwlock_t

  3. 根据应用程序的运行情况,请考虑使用条件变量(pthread lib impl:pthread_cond_t)。这实际上是从一个线程到另一个线程的信号,这可以让你删除你的while循环和bool检查。

答案 4 :(得分:-2)

使boolean volatile就足够了(在x86架构上),不需要互斥锁:

volatile bool b;