当不同的线程仅使用不相关的对象并且实际上不共享任何东西时,它们就不会具有竞争条件,对吗?显然。
实际上,所有线程共享一些东西:地址空间。无法保证一个线程使用的内存位置不会在其他时间分配给另一个线程。对于动态分配的对象甚至自动对象的内存,情况可能都是如此:没有规定多个线程的“堆栈”(函数的本地对象)的内存空间是预先分配的(甚至是延迟的),不相交的并且表示为通常的线性“堆栈”;它可能是具有堆栈(FILO)行为的任何东西。因此,用于存储自动对象的内存位置以后可以由另一个线程中的另一个自动对象重用。
这本身似乎是无害且无趣的,因为如何为自动对象腾出空间仅在缺少空间(非常大的自动数组或深度递归)时才重要。
关于同步呢?显然,不相关的不相交线程不可以使用任何C ++同步原语来确保正确的同步,因为按照定义,没有任何要同步的(上),因此不会在线程之间创建之前关系。
如果实现在销毁局部变量并退出线程1中的foo()
以存储变量之后,重新使用i
堆栈的内存范围(包括foo()
的位置)怎么办?用于线程2中的bar()
?
void foo() { // in thread 1
int i;
i = 1;
}
void bar() { // in thread 2
int i;
i = 2;
}
在i = 1
和i = 2
之间没有发生。
会导致数据争用和不确定的行为吗?
换句话说,所有多线程程序是否有可能基于用户无法控制的实现选择而具有不确定的行为,这些选择是不可预见的,并且由于种族问题他无能为力?
答案 0 :(得分:4)
C ++内存模型的行为不像您直觉地期望的那样。例如,它具有内存位置,但引用了N4713草案第6.6.1节第3段:
内存位置是标量类型的对象或相邻位域的最大序列,所有这些均具有 非零宽度。 [注意:语言的各种功能(例如引用和虚函数)可能涉及 程序无法访问但由实现管理的其他内存位置。 — 尾注]两个或多个执行线程(6.8.2)可以访问单独的内存位置,而不会造成干扰 彼此。
因此,根据C ++内存模型,即使在物理计算机级别上,一个线程在另一个RAM释放之后分配在同一RAM中,也永远不会认为不同线程中的两个不同对象具有相同的内存位置。
通过C ++内存模型,您所询问的情况不是数据争用。无论硬件的内存模型如何,实现都必须采取必要的步骤来确保这种安全性。
答案 1 :(得分:2)
物理机的“相同地址”与C ++内存模型无关。 C ++内存模型讨论了抽象机的行为。即使在不同的时间具有相同的机器地址,抽象机器中的地址也可能在根本上无法比拟。
C ++抽象机中的种族条件只讨论其中的操作,而不是物理机器上的操作。编译器的工作是确保C ++代码的抽象机行为的物理机实现是一致的。
如果它做一些奇怪的事情,例如在线程之间重用堆栈地址空间,那么它会做任何必须做的事情,以保持缺少访问抽象机中无关变量的竞争条件。这些都不会在C ++代码级别上发生。不涉及任何C ++代码(可能不在namespace std
中)。