在学习多执行绪编程时,我撰写了以下代码。
#include <thread>
#include <iostream>
#include <cassert>
void check() {
int a = 0;
int b = 0;
{
std::jthread t2([&](){
int i = 0;
while (a >= b) {
i;
}
std::cout << "failed at iteration " << i << "\n"
// I know at this point a and b may have changed
<< a << " >= " << b << "\n";
std::exit(0);
});
std::jthread t1([&](){
while (true) {
a;
b;
}
});
}
}
int main() {
check();
}
由于 a
总是发生在 b
a 应该总是大于或等于 b 之前。但实验表明,有时b > a
. 为什么?是什么原因造成的?我该如何强制执行?
即使我替换int a = 0;
为int a = 1000;
which 使所有这一切变得更加疯狂。
程序很快退出,因此不会发生 int 溢位。我没有发现可能导致这种情况的汇编中重新排序的说明。
uj5u.com热心网友回复:
因为 a 总是在 ba 之前发生,所以应该总是大于或等于 b
仅在其执行执行绪中。并且仅当执行执行绪可以观察到时。
C 需要某些显式的“同步”,以便其他执行执行绪可以看到一个执行执行绪所做的更改。
a;
b;
仅凭这些陈述句,该执行执行绪实际上无法“区分”是先递增a
还是b
先递增。因此,C 允许编译器实作它想要的任何优化或代码重新排序步骤,只要它在其执行执行绪中没有可观察到的效果,并且如果实际生成的代码b
首先递增,则不会有任何可观察到的效果。这个执行执行绪不可能分辨出差异。
但是,如果有一些中间陈述句“查看”了a
,那么这将不再成立,并且编译器将需要a
在以某种方式使用它之前实际生成递增的代码。
这只是这个执行执行绪,单独存在。即使可以观察到a
在b
此执行执行绪中更改的相对顺序,根据标准,C 编译器也被允许以任何顺序实际递增实际变量,只要还有任何其他调整使此不可观察。但它可以被另一个执行执行绪观察到。为了防止这种情况发生,有必要采取显式同步步骤,使用互斥锁、条件变量和 C 执行执行绪模型的其他部分。
uj5u.com热心网友回复:
在这些不同变量的增量和读取它们之间存在非平凡的竞争条件。如果您想对这些读取和写入进行严格排序,您将不得不使用某种同步机制。std::atomic<> 使它更容易。
试试这个:
#include<iostream>
#include <thread>
#include <iostream>
#include <cassert>
#include <atomic>
void check() {
struct AB { int a = 0; int b=0; };
std::atomic<AB> ab;
{
std::jthread t2([&](){
int i = 0;
AB temp;
while (true) {
temp = ab;
if ( temp.a > temp.b ) break;
i;
}
std::cout << "failed at iteration " << i << "\n"
// I know at this point a and b may have changed
<< temp.a << " >= " << temp.b << "\n";
std::exit(0);
});
std::jthread t1([&](){
while (true) {
AB temp = ab;
temp.a ;
temp.b ;
ab = temp;
}
});
}
}
int main() {
check();
}
代码:https : //godbolt.org/z/Kxeb8d8or
结果:
Program returned: 143
Program stderr
Killed - processing time exceeded
0 评论