原子线程栅栏:为什么在这个非原子变量上存在数据竞争?这有关系吗?
atomic thread fence: Why is there a data race on this non atomic variable? And does it matter?
假设我们有 2 个线程。一个生产者和一个消费者。我们有生产数据的生产者,以及使用这些数据的消费者。然而守卫不是原子的!
bool isDataReady = false;
int data = 0;
void Producer() {
data = 42;
std::atomic_thread_fence(std::memory_order_release);
isDataReady = true;
}
void Consumer() {
while(!isDataReady);
std::atomic_thread_fence(std::memory_order_acquire);
assert(data == 42);
}
我想知道为什么 isDataReady
上存在数据竞争。
通常,正确的代码应该是对原子 bool 变量使用 relaxed
排序。
是不是因为isDataReady的写(事务)在读之前没有完成?即使是这样,这真的是个问题吗?
TL;DR
这种数据竞争很危险,您应该注意消除它。它可能不会因为你的运气而表现出来,但它最终会让人头疼。
长一点
由于以下几个问题,此代码存在问题:
虽然编译 Consumer
编译器不知道 isDataReady
可以在后台更改,因此发出 while(!isDataReady)
无限循环或什么都不发出是完全合理的(由于 forward progress guarantee,正如评论中指出的那样)。
如果写入 and/or 读取到 bool
不是原子的(在大多数平台上不是这种情况,但理论上是可能的)任何读取都可能导致获取垃圾数据.
带有 std::memory_order_release
的内存栅栏确保线程中发生的更改在其他线程调用带有 std::memory_order_acquire
的栅栏后可见(至少在简化中)。因此,bool变量的变化在其他线程中可能是不可见的。
由于现代处理器的超标量架构,操作可能会在运行时由处理器重新排序。因此从 Consumer
可见的 Producer
中的内存写入顺序可能与代码中的顺序不同。
假设我们有 2 个线程。一个生产者和一个消费者。我们有生产数据的生产者,以及使用这些数据的消费者。然而守卫不是原子的!
bool isDataReady = false;
int data = 0;
void Producer() {
data = 42;
std::atomic_thread_fence(std::memory_order_release);
isDataReady = true;
}
void Consumer() {
while(!isDataReady);
std::atomic_thread_fence(std::memory_order_acquire);
assert(data == 42);
}
我想知道为什么 isDataReady
上存在数据竞争。
通常,正确的代码应该是对原子 bool 变量使用 relaxed
排序。
是不是因为isDataReady的写(事务)在读之前没有完成?即使是这样,这真的是个问题吗?
TL;DR
这种数据竞争很危险,您应该注意消除它。它可能不会因为你的运气而表现出来,但它最终会让人头疼。
长一点
由于以下几个问题,此代码存在问题:
虽然编译
Consumer
编译器不知道isDataReady
可以在后台更改,因此发出while(!isDataReady)
无限循环或什么都不发出是完全合理的(由于 forward progress guarantee,正如评论中指出的那样)。如果写入 and/or 读取到
bool
不是原子的(在大多数平台上不是这种情况,但理论上是可能的)任何读取都可能导致获取垃圾数据.带有
std::memory_order_release
的内存栅栏确保线程中发生的更改在其他线程调用带有std::memory_order_acquire
的栅栏后可见(至少在简化中)。因此,bool变量的变化在其他线程中可能是不可见的。由于现代处理器的超标量架构,操作可能会在运行时由处理器重新排序。因此从
Consumer
可见的Producer
中的内存写入顺序可能与代码中的顺序不同。