gpt4 book ai didi

c++ - 栅栏实际上如何在 C++ 中工作

转载 作者:行者123 更新时间:2023-11-30 03:46:14 24 4
gpt4 key购买 nike

我一直在努力理解栅栏实际上是如何强制代码同步的。

例如,假设我有这段代码

bool x = false;
std::atomic<bool> y;
std::atomic<int> z;
void write_x_then_y()
{
x = true;
std::atomic_thread_fence(std::memory_order_release);
y.store(true, std::memory_order_relaxed);
}
void read_y_then_x()
{
while (!y.load(std::memory_order_relaxed));
std::atomic_thread_fence(std::memory_order_acquire);
if (x)
++z;
}
int main()
{
x = false;
y = false;
z = 0;
std::thread a(write_x_then_y);
std::thread b(read_y_then_x);
a.join();
b.join();
assert(z.load() != 0);
}

因为释放栅栏之后是一个原子存储操作,而获取栅栏之前是一个原子加载,所以一切都按预期同步并且断言不会触发

但如果 y 不是这样的原子变量

bool x;
bool y;
std::atomic<int> z;
void write_x_then_y()
{
x = true;
std::atomic_thread_fence(std::memory_order_release);
y = true;
}
void read_y_then_x()
{
while (!y);
std::atomic_thread_fence(std::memory_order_acquire);
if (x)
++z;
}

然后,我听说,可能会出现数据竞争。但这是为什么呢?为什么必须在释放栅栏之后进行原子存储,并且在获取栅栏之前进行原子加载才能使代码正确同步?

如果有人能提供一个数据竞争导致断言触发的执行场景,我将不胜感激

最佳答案

您的第二个代码段没有真正的数据竞争问题。如果编译器从字面上从编写的代码中生成机器代码,那么这段代码就可以了。

但编译器可以自由生成任何机器码,这在单线程程序的情况下等同于原始机器码。

例如,编译器可以注意到,y变量在while(!y)循环中不会改变,所以它可以加载这个变量一次来注册和使用只有在下一次迭代中注册的。因此,如果最初 y=false,您将得到一个无限循环。

另一个可能的优化是删除 while(!y) 循环,因为它不包含对 volatileatomic< 的访问/em> 变量并且不使用同步 操作。 (C++ 标准规定任何正确的程序都应该最终执行上面指定的操作之一,因此编译器在优化程序时可能会依赖于该事实)。

等等。

更一般地说,C++ 标准指定并发访问任何非原子变量会导致未定义的行为,这就像“保修”被清除”。这就是为什么您应该使用 atomic y 变量。

从另一方面来说,变量 x 不需要是原子的,因为由于内存栅栏,对它的访问不是并发的。

关于c++ - 栅栏实际上如何在 C++ 中工作,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/34190291/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com