3

我读过一些类似的问题,但那里描述的情况要复杂一些。

我在堆和两个线程中有一个bool b初始化。false我确实理解boolsare 的操作not atomic,但请阅读问题直到最后。

第一个线程只能设置b = true一次,并且不做任何其他事情。第二个线程b在一个循环中检查,如果它true做了一些动作。

我是否需要使用一些同步机制(如互斥锁)来保护b?如果我不这样做会发生什么?当ints我同时读写时,我显然可以得到任意值。但是有了boolsjust trueandfalse我不介意一次得到false而不是true. 有潜力SIGSEGV吗?

4

5 回答 5

7

数据竞争导致未定义的行为。就标准而言,允许符合标准的实现出现段错误。

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

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

无论如何,阻止在软件中进行优化并不能阻止在具有非一致缓存的系统上的硬件中发生相同的事情(至少,所以我声称:其他人认为这是不正确的,并且需要volatile访问通过缓存读/写。一些实现确实是这样)。如果您询问标准的内容,那么硬件是否无限期地向您显示陈旧的缓存并不重要,因为行为仍然未定义,因此无论此特定优化是否是问题,实现都可能破坏您的代码这打破了它。

于 2012-10-30T09:00:55.990 回答
3

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

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

于 2012-10-30T09:03:11.873 回答
2

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

如果你不这样做,你就有了数据竞赛。具有数据竞争的程序具有未定义的行为。这个问题的答案与“你希望你的程序有明确定义的行为吗?”这个问题的答案是一样的。

如果我不这样做会发生什么?

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

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

于 2012-10-30T09:01:33.800 回答
0

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

  1. 使用互斥锁,详细信息已在上面的其他答案中介绍。

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

  3. 根据您的应用程序正在做什么,考虑使用条件变量(pthread lib impl: pthread_cond_t)。这实际上是从一个线程到另一个线程的信号,它可以让您删除您的 while 循环和布尔检查。

于 2012-10-30T09:47:20.727 回答
-2

使布尔值 volatile 就足够了(在 x86 架构上),不需要互斥体:

volatile bool b;
于 2012-10-30T09:07:45.177 回答