简体   繁体   English

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

[英]Dangers of simultaneous write and read of a boolean in a simple situation

I've read some similar questions but the situations described there are bit more complicated. 我读过一些类似的问题,但是那里描述的情况更加复杂。

I have a bool b initialized as false in the heap and two threads. 我在堆和两个线程中都将bool b初始化为false I do understand that operations with bools are not atomic , but please read the question till the end. 我确实了解使用bools进行操作not atomic ,但请bools阅读问题。

First thread can set b = true only once and doesn't do anything else with it. 第一个线程只能将b = true设置一次,并且不会对其执行任何其他操作。 Second thread checks b in a loop and if it's true does some actions. 第二个线程在循环中检查b ,如果它是true则执行一些操作。

Do I need to use some synchronization mechanism(like mutexes) to protect b ? 我是否需要使用一些同步机制(例如互斥体)来保护b What can happen if I don't? 如果我不这样做会怎样? With ints I can obviously get arbitrary values when I read and write in the same time. 使用ints时,显然我可以在同时读写时获得任意值。 But with bools there are just true and false and I don't mind to once get false instead of true . 但随着bools刚好有truefalse ,我不介意一次拿到false ,而不是true Is it a potential SIGSEGV ? 这是潜在的SIGSEGV吗?

Data races result in undefined behavior. 数据争用导致不确定的行为。 As far as the standard is concerned, a conforming implementation is permitted to segfault. 就该标准而言,允许采用兼容的实现来进行段错误。

In practice the main danger is that without synchronization, the compiler will observe enough of the code in the reader loop to judge that b "never changes", and optimize out all but the first read of the value. 实际上,主要的危险是没有同步,编译器将在阅读器循环中观察到足够多的代码,以判断b “从不改变”,并优化除值的首次读取以外的所有值。 It can do this because if it observes that there is no synchronization in the loop, then it knows that any write to the value would be a data race. 之所以可以这样做是因为,如果它观察到循环中没有同步,那么它将知道对该值的任何写操作都会导致数据争用。 The optimizer is permitted to assume that your program does not provoke undefined behavior, so it is permitted to assume that there are no writes from other threads. 允许优化程序假定您的程序不会引发未定义的行为,因此可以假定优化程序假设没有其他线程进行写操作。

Marking b as volatile will prevent this particular optimization in practice, but even on volatile objects data races are undefined behavior. 在实践中将b标记为volatile将阻止这种特定的优化,但是即使在volatile对象上,数据竞争也是不确定的行为。 Calling into code that the optimizer "can't see" will also prevent the optimization in practice, since it doesn't know whether that code modifies b . 调用优化器“看不到”的代码也会在实践中阻止优化,因为它不知道该代码是否会修改b Of course with link-time/whole-program optimization there is less that the optimizer can't see, than with compile-time-only optimization. 当然,对于链接时/整个程序优化,与仅编译时优化相比,优化器看不到的要少。

Anyway, preventing the optimization from being made in software doesn't prevent the equivalent thing happening in hardware on a system with non-coherent caches (at least, so I claim: other people argue that this is not correct, and that volatile accesses are required to read/write through caches. Some implementations do behave that way). 无论如何,阻止在软件中进行优化并不能阻止在具有非一致性缓存的系统上的硬件中发生同样的事情(至少,因此,我声称:其他人认为这是不正确的,并且volatile访问是读取/写入缓存所需的内容。某些实现的行为确实如此)。 If you're asking about what the standard says then it doesn't really matter whether or not the hardware shows you a stale cache indefinitely, since behavior remains undefined and so the implementation can break your code regardless of whether this particular optimization is the thing that breaks it. 如果您要问标准说了什么,那么硬件是否会无限期地向您显示过时的缓存并不重要,因为行为仍然是不确定的,因此实现可能会破坏您的代码,而不管这种特殊的优化是什么。打破了它。

The problem you might get is that we don't know how long it takes for the reader thread to see the changed value. 可能会遇到的问题是,我们不知道读取器线程需要多长时间才能看到更改的值。 If they are on different CPUs, with separate caches, there are no guarantees unless you use a memory barrier to synchronize the caches. 如果它们位于具有不同缓存的不同CPU上,则无法保证,除非您使用内存屏障来同步缓存。

On an x86 this is handled automatically by the hardware protocol, but not on some other systems. 在x86上,这是由硬件协议自动处理的,但是在某些其他系统上则不会。

Do I need to use some synchronization mechanism(like mutexes) to protect b ? 我是否需要使用一些同步机制(例如互斥体)来保护b

If you don't, you have a data race. 如果不这样做,就意味着数据竞争。 Programs with data races have undefined behaviour. 具有数据争用的程序具有未定义的行为。 The answer to this question is the same as the answer to the question "Do you want your program to have well-defined behaviour?" 该问题的答案与以下问题的答案相同:“您是否希望程序具有明确定义的行为?”

What can happen if I don't? 如果我不这样做会怎样?

Theoretically, anything can happen. 从理论上讲,任何事情都可能发生。 That's what undefined behaviour means. 这就是未定义行为的意思。 The most likely bad thing that can happen is that the "second thread" may never see a true value. 可能发生的最坏的事情是“第二个线程”可能永远看不到true值。

The compiler can assume that a program has no data races (if it has the behaviour is not defined by the standard, so behaving as if it didn't is fine). 编译器可以假定程序没有数据争用(如果程序的行为不是标准定义的,那么表现就好像没有问题)。 Since the second thread only ever reads from a variable that has the value false , and there's no synchronization that affects those reads, the logical conclusion is that the value never changes, and thus the loop is infinite. 由于第二个线程仅读取具有值false的变量,并且不存在影响这些读取的同步,因此逻辑结论是该值永不变,因此循环是无限的。 (and some infinite loops have undefined behaviour in C++11!) (并且某些无限循环在C ++ 11中具有未定义的行为!)

Here are a few alternative solutions: 以下是一些替代解决方案:

  1. Use a Mutex, details have been covered in the other answers above. 使用Mutex,上面的其他答案已涵盖了细节。

  2. Consider using a read/write lock which will manage/protect simultaneous reads and writes. 考虑使用读/写锁,它将管理/保护同时进行的读和写。 The pthread lib provides an implementation: pthread_rwlock_t pthread库提供了一个实现:pthread_rwlock_t

  3. Depending on what your application is doing, consider using a condition variable (pthread lib impl: pthread_cond_t). 根据您的应用程序在做什么,请考虑使用条件变量(pthread lib impl:pthread_cond_t)。 This is effectively a signal from one thread to another, which could allow you to remove your while loop and bool checking. 这实际上是从一个线程到另一个线程的信号,它可以让您删除while循环和布尔检查。

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

volatile bool b;

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM