在简单情况下,同时写并阅读布尔值的危险

Dangers of simultaneous write and read of a boolean in a simple situation

本文关键字:布尔值 危险 情况下 简单      更新时间:2023-10-16

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

i在堆中有一个bool bfalse,并且有两个线程。我确实知道使用bools的操作是not atomic,但请阅读该问题直到最后。

第一个线程只能设置b = true一次,并且对此不做任何其他操作。第二个线程在循环中检查b,如果是true,请执行一些操作。

我是否需要使用一些同步机制(例如静音)来保护b?如果我不这样做会发生什么?使用ints,当我同时阅读和写入时,显然可以获得任意值。但是使用bools只有truefalse,我不介意一次获得false而不是true。这是潜在的SIGSEGV

数据种族导致不确定的行为。就标准而言,允许实施一个符合的实现。

实际上,主要危险是,如果没有同步,编译器将在读取器循环中观察到足够多的代码,以判断b"永不更改",并优化除了第一个值之外的所有内容。它可以做到这一点,因为如果它观察到循环中没有同步,那么它知道任何写入值的写入都是数据竞赛。允许优化器假设您的程序不会引起不确定的行为,因此可以假设没有其他线程的写作。

b标记为volatile将在实践中阻止这种特殊的优化,但是即使在volatile上,数据种族也是不确定的行为。调用优化器"看不到"的代码也将在实践中阻止优化,因为它不知道该代码是否修改了b。当然,通过链接时间/全程图优化,优化器看不到,而不是仅通过编译时间优化。

无论如何,防止在软件中进行优化并不能阻止与具有非连接缓存的系统上硬件中发生的等效事物(至少,我声称:其他人认为这是不正确的,并且volatile访问需要通过缓存读/写入。某些实现的行为确实如此)。如果您询问标准所说的内容,那么硬件是否会无限期地显示出陈旧的缓存并不重要,因为行为仍然不确定,因此实现可能会破坏您的代码/em>优化是打破它的东西。

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

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

我需要使用一些同步机制(例如静音)来保护b

如果不这样做,则有数据竞赛。具有数据竞赛的程序具有不确定的行为。这个问题的答案与"您希望您的程序具有明确的行为?"的答案相同。

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

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

编译器可以假定程序没有数据竞赛(如果其行为不是由标准定义的,那么行为就好像不正常了)。由于第二个线程仅从具有值false的变量读取,并且没有影响这些读取的同步,因此逻辑上的结论是该值永远不会改变,因此循环是无限的。(并且某些无限环在C 11中具有不确定的行为!)

这是一些替代解决方案:

  1. 使用静音,在上面的其他答案中介绍了详细信息。

  2. 考虑使用读/写锁定,该锁可以管理/保护同时读取和写入。Pthread Lib提供了一个实现:PTHREAD_RWLOCK_T

  3. 取决于您的应用程序的操作,请考虑使用条件变量(Pthread Lib Ingl:pthread_cond_t)。这实际上是一个从一个线程到另一个线程的信号,这可以使您可以删除循环并进行检查。

使布尔挥发性足够(在x86体系结构上),无需静音:

volatile bool b;