multithreading 在并发编程的上下文中,“数据竞争”和“竞争条件”实际上是同一回事吗
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/11276259/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Are "data races" and "race condition" actually the same thing in context of concurrent programming
提问by Inquisitive
I often find these terms being used in context of concurrent programming . Are they the same thing or different ?
我经常发现这些术语用于并发编程的上下文中。它们是相同的还是不同的?
回答by Baris Kasikci
No, they are not the same thing. They are not a subset of one another. They are also neither the necessary, nor the sufficient condition for one another.
不,它们不是一回事。它们不是彼此的子集。它们也既不是彼此的必要条件,也不是充分条件。
The definition of a data race is pretty clear, and therefore, its discovery can be automated. A data race occurs when 2 instructions from different threads access the same memory location, at least one of these accesses is a write and there is no synchronization that is mandating anyparticular order among these accesses.
数据竞争的定义非常明确,因此可以自动发现它。当来自不同线程的 2 条指令访问同一内存位置时,就会发生数据竞争,这些访问中至少有一个是写操作,并且在这些访问之间没有强制要求任何特定顺序的同步。
A race condition is a semantic error. It is a flaw that occurs in the timing or the ordering of events that leads to erroneous program behavior. Many race conditions can be caused by data races, but this is not necessary.
竞争条件是语义错误。发生在时间或事件顺序中的缺陷会导致错误的程序行为。许多竞争条件可能由数据竞争引起,但这不是必需的。
Consider the following simple example where x is a shared variable:
考虑以下简单示例,其中 x 是共享变量:
Thread 1 Thread 2
lock(l) lock(l)
x=1 x=2
unlock(l) unlock(l)
In this example, the writes to x from thread 1 and 2 are protected by locks, therefore they are always happening in some order enforced by the order with which the locks are acquired at runtime. That is, the writes' atomicity cannot be broken; there is always a happens before relationship between the two writes in any execution. We just cannot know which write happens before the other a priori.
在此示例中,从线程 1 和 2 对 x 的写入受锁保护,因此它们总是以某种顺序发生,由在运行时获取锁的顺序强制执行。也就是说,写入的原子性不能被破坏;在任何执行中,两次写入之间的关系之前总会发生。我们只是无法先验地知道哪个写入发生在另一个之前。
There is no fixed ordering between the writes, because locks cannot provide this. If the programs' correctness is compromised, say when the write to x by thread 2 is followed by the write to x in thread 1, we say there is a race condition, although technically there is no data race.
写入之间没有固定的顺序,因为锁不能提供这一点。如果程序的正确性受到损害,比如说当线程 2 写入 x 之后是线程 1 写入 x 时,我们说存在竞争条件,尽管从技术上讲不存在数据竞争。
It is far more useful to detect race conditions than data races; however this is also very difficult to achieve.
检测竞争条件比检测数据竞争要有用得多;然而,这也很难实现。
Constructing the reverse example is also trivial. Thisblog post also explains the difference very well, with a simple bank transaction example.
构建反向示例也很简单。这篇博文也很好地解释了差异,举了一个简单的银行交易示例。
回答by Marko Topolnik
According to Wikipedia, the term "race condition" has been in use since the days of the first electronic logic gates. In the context of Java, a race condition can pertain to any resource, such as a file, network connection, a thread from a thread pool, etc.
根据维基百科,自第一个电子逻辑门时代以来,术语“竞争条件”就一直在使用。在 Java 的上下文中,竞争条件可以与任何资源有关,例如文件、网络连接、线程池中的线程等。
The term "data race" is best reserved for its specific meaning defined by the JLS.
术语“数据竞争”最好保留为JLS定义的特定含义。
The most interesting case is a race condition that is very similar to a data race, but still isn't one, like in this simple example:
最有趣的情况是与数据竞争非常相似的竞争条件,但仍然不是,就像这个简单的例子:
class Race {
static volatile int i;
static int uniqueInt() { return i++; }
}
Since i
is volatile, there is no data race; however, from the program correctness standpoint there is a race condition due to the non-atomicity of the two operations: read i
, write i+1
. Multiple threads may receive the same value from uniqueInt
.
由于i
是 volatile,没有数据竞争;然而,从程序正确性的角度来看,由于两个操作的非原子性,存在竞争条件: read i
, write i+1
。多个线程可能会从uniqueInt
.
回答by Shirgill Farhan
No, they are different & neither of them is a subsetof one or vice-versa.
不,它们是不同的,它们都不是一个的子集,反之亦然。
The term race condition is often confused with the related term data race, which arises when synchronization is not used to coordinate all access to a shared nonfinal field. You risk a data race whenever a thread writes a variable that might next be read by another thread or reads a variable that might have last been written by another thread if both threads do not use synchronization; code with data races has no useful defined semantics under the Java Memory Model. Not all race conditions are data races, and not all data races are race conditions, but they both can cause concurrent programs to fail in unpredictable ways.
术语竞争条件经常与相关术语数据竞争混淆,数据竞争是在不使用同步来协调对共享非最终字段的所有访问时出现的。每当一个线程写入一个可能被另一个线程读取的变量或读取一个可能最后被另一个线程写入的变量(如果两个线程都没有使用同步)时,您就会冒着数据竞争的风险;带有数据竞争的代码在 Java 内存模型下没有有用的定义语义。并非所有的竞争条件都是数据竞争,也不是所有的数据竞争都是竞争条件,但它们都可能导致并发程序以不可预测的方式失败。
Taken from the excellent book - Java Concurrency in Practice by Joshua Bloch & Co.
摘自Joshua Bloch & Co的优秀书籍 - Java Concurrency in Practice。
回答by Xiao-Feng Li
TL;DR: The distinction between data race and race condition depends on the nature of problem formulation, and where to draw the boundary between undefined behavior and well-defined but indeterminate behavior. The current distinction is conventional and best reflects the interface between processor architect and programming language.
TL;DR:数据竞争和竞争条件之间的区别取决于问题表述的性质,以及未定义行为和定义明确但不确定的行为之间的界限在哪里。当前的区别是常规的,最能反映处理器架构和编程语言之间的接口。
1. Semantics
1. 语义
Data race specifically refers to the non-synchronized conflicting "memory accesses" (or actions, or operations) to the same memory location. If there is no conflict in the memory accesses, while there is still indeterminate behavior caused by operation ordering, that is a race condition.
数据竞争特指对同一内存位置的非同步冲突“内存访问”(或动作或操作)。如果内存访问没有冲突,而仍然存在操作顺序导致的不确定行为,那就是竞争条件。
Note "memory accesses" here have specific meaning. They refer to the "pure" memory load or store actions, without any additional semantics applied. For example, a memory store from one thread does not (necessarily) know how long it takes for the data to be written into the memory, and finally propagates to another thread. For another example, a memory store to one location before another store to another location by the same thread does not (necessarily) guarantee the first data written in the memory be ahead of the second. As a result, the order of those pure memory accesses are not (necessarily) able to be "reasoned", and anything could happen, unless otherwise well defined.
注意这里的“内存访问”有特定的含义。它们指的是“纯”内存加载或存储操作,没有应用任何额外的语义。例如,来自一个线程的内存存储(不一定)知道将数据写入内存并最终传播到另一个线程需要多长时间。再举一个例子,同一线程先将内存存储到一个位置,然后再将另一个位置存储到另一个位置,这并不能(必然)保证写入内存的第一个数据在第二个之前。结果,这些纯内存访问的顺序(必然)无法“合理化”,任何事情都可能发生,除非另有明确定义。
When the "memory accesses" are well defined in terms of ordering through synchronization, additional semantics can ensure that, even if the timing of the memory accesses are indeterminate, their order can be "reasoned"through the synchronizations. Note, although the ordering between the memory accesses can be reasoned, they are not necessarily determinate, hence the race condition.
当“内存访问”在通过同步的排序方面被很好地定义时,额外的语义可以确保,即使内存访问的时间不确定,它们的顺序也可以通过同步“合理化”。请注意,虽然可以推断内存访问之间的顺序,但它们不一定是确定的,因此存在竞争条件。
2. Why the difference?
2. 为何不同?
But if the order is still indeterminate in race condition, why bother to distinguish it from data race? The reason is in practical rather than theoretical. It is because the distinction does exist in the interface between the programming language and processor architecture.
但是如果顺序在竞争条件下仍然是不确定的,为什么要费心把它和数据竞争区分开来呢?原因是实际的而不是理论上的。这是因为编程语言和处理器架构之间的接口确实存在区别。
A memory load/store instruction in modern architecture is usually implemented as "pure" memory access, due to the nature of out-of-order pipeline, speculation, multi-level of cache, cpu-ram interconnection, especially multi-core, etc. There are lots of factors leading to indeterminate timing and ordering. To enforce ordering for every memory instruction incurs huge penalty, especially in a processor design that supports multi-core. So the ordering semantics are provided with additional instructions like various barriers (or fences).
由于乱序流水线、推测、多级缓存、cpu-ram 互连,尤其是多核等特性,现代架构中的内存加载/存储指令通常被实现为“纯”内存访问. 有很多因素导致不确定的时间和顺序。强制对每条内存指令进行排序会导致巨大的损失,尤其是在支持多核的处理器设计中。因此,排序语义提供了额外的指令,如各种障碍(或围栏)。
Data race is the situation of processor instruction execution without additional fences to help reasoning the ordering of conflicting memory accesses. The result is not only indeterminate, but also possibly very weird, e.g., two writes to the same word location by different threads may result with each writing half of the word, or may only operate upon their locally cached values. -- These are undefined behavior, from the programmer's point of view. But they are (usually) well defined from the processor architect's point of view.
数据竞争是处理器指令执行的情况,没有额外的栅栏来帮助推理冲突内存访问的顺序。结果不仅不确定,而且可能非常奇怪,例如,不同线程对同一字位置的两次写入可能会导致每次写入一半的字,或者可能仅对其本地缓存的值进行操作。-- 从程序员的角度来看,这些是未定义的行为。但从处理器架构师的角度来看,它们(通常)是明确定义的。
Programmers have to have a way to reasontheir code execution. Data race is something they cannot make sense, therefore should always avoid (normally). That is why the language specifications that are low level enough usually define data race as undefined behavior, different from the well-defined memory behavior of race condition.
程序员必须有一种方法来推理他们的代码执行。数据竞争是他们无法理解的事情,因此应该始终避免(通常)。这就是为什么足够低级别的语言规范通常将数据竞争定义为未定义的行为,与竞争条件的明确定义的内存行为不同。
3. Language memory models
3. 语言记忆模型
Different processors may have different memory access behavior, i.e., processor memory model. It is awkward for programmers to study the memory model of every modern processor and then develop programs that can benefit from them. It is desirable if the language can define a memory model so that the programs of that language always behave as expected as the memory model defines. That is why Java and C++ have their memory models defined. It is the burden of the compiler/runtime developers to ensure the language memory models are enforced across different processor architectures.
不同的处理器可能有不同的内存访问行为,即处理器内存模型。程序员研究每个现代处理器的内存模型然后开发可以从中受益的程序是很尴尬的。如果语言可以定义内存模型,那么该语言的程序总是按照内存模型定义的预期运行,这是可取的。这就是 Java 和 C++ 定义了它们的内存模型的原因。编译器/运行时开发人员的负担是确保跨不同的处理器架构强制执行语言内存模型。
That said, if a language does not want to expose the low level behavior of the processor (and is willing to sacrifice certain performance benefits of the modern architectures), they can choose to define a memory model that completely hide the details of "pure" memory accesses, but apply ordering semantics for all their memory operations. Then the compiler/runtime developers may choose to treat every memory variable as volatile in all processor architectures. For these languages (that support shared memory across threads), there are no data races, but may still be race conditions, even with a language of complete sequential consistence.
也就是说,如果一种语言不想暴露处理器的低级行为(并且愿意牺牲现代架构的某些性能优势),他们可以选择定义一个完全隐藏“纯”细节的内存模型内存访问,但对其所有内存操作应用排序语义。然后编译器/运行时开发人员可以选择将每个内存变量视为所有处理器架构中的易失性变量。对于这些语言(支持跨线程共享内存),不存在数据竞争,但可能仍然存在竞争条件,即使使用完全顺序一致性的语言。
On the other hand, the processor memory model can be stricter (or less relaxed, or at higher level), e.g., implementing sequential consistency as early-days processor did. Then all memory operations are ordered, and no data race exists for any languages running in the processor.
另一方面,处理器内存模型可以更严格(或不那么宽松,或在更高级别),例如,像早期处理器那样实现顺序一致性。然后所有内存操作都是有序的,处理器中运行的任何语言都不存在数据竞争。
4. Conclusion
4。结论
Back to the original question, IMHO it is fine to define data race as a special case of race condition, and race condition at one level may become data race at a higher level. It depends on the nature of problem formulation, and where to draw the boundary between undefined behavior and well-defined but indeterminate behavior. Just the current convention defines the boundary at language-processor interface, does not necessarily mean that is always and must be the case; but the current convention probably best reflects the state-of-the-art interface (and wisdom) between processor architect and programming language.
回到最初的问题,恕我直言,将数据竞争定义为竞争条件的一种特殊情况是可以的,一个级别的竞争条件可能会成为更高级别的数据竞争。这取决于问题表述的性质,以及未定义行为与定义明确但不确定的行为之间的界限在哪里。只是当前的约定定义了语言处理器接口的边界,并不一定意味着总是并且必须如此;但是当前的约定可能最好地反映了处理器架构师和编程语言之间的最新接口(和智慧)。