我找到了一个我能够在 linux 中的 g++
下重现的竞争条件示例。我不明白的是操作顺序在此示例中的重要性。
int va = 0;
void fa() {
for (int i = 0; i < 10000; ++i)
++va;
}
void fb() {
for (int i = 0; i < 10000; ++i)
--va;
}
int main() {
std::thread a(fa);
std::thread b(fb);
a.join();
b.join();
std::cout << va;
}
如果我使用 va = va + 1;
,我可以理解顺序很重要,因为 RHS va
可能在返回指定的 LHS 之前发生变化va
。有人可以澄清一下吗?
最佳答案
标准说(引用最新草案):
[intro.races]
Two expression evaluations conflict if one of them modifies a memory location ([intro.memory]) and the other one reads or modifies the same memory location.
The execution of a program contains a data race if it contains two potentially concurrent conflicting actions, at least one of which is not atomic, and neither happens before the other, except for the special case for signal handlers described below. Any such data race results in undefined behavior.
您的示例程序存在数据竞争,并且程序的行为未定义。
What I don't understand is how the order of operations matter in this example.
操作顺序很重要,因为操作不是原子操作,它们读取和修改相同的内存位置。
can undertand that the order matters if I had used va = va + 1; because then RHS va could have changed before getting back to assigned LHS va
这同样适用于增量运算符。抽象机将:
- 从内存中读取一个值
- 增值
- 将一个值写回内存
那里有多个步骤可以与另一个线程中的操作交错。
即使每个线程只有一个操作,也不能保证定义良好的行为,除非这些操作是原子的。
C++ 范围之外的注意事项:CPU 可能只有一条指令用于递增内存中的整数。比如x86就有这样的指令。它可以以原子方式和非原子方式调用。除非您在 C++ 中显式使用原子操作,否则编译器使用原子指令是一种浪费。
关于c++ - 在 C++ 中递增和递减全局变量时的竞争条件,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/57793739/