通常,在原子加载之后使用获取栅栏:
if (flag.load(std::memory_order_relaxed)) {
std::atomic_thread_fence(std::memory_order_acquire);
// Read some non-atomic data here.
}
但是,在原子加载之前使用获取栅栏是否有意义?
在下面的示例中,缓冲区data
和原子布尔标志在
threadA
和
threadB
之间共享。
data
缓冲区最初包含全零。
threadA
将向其写入非零值,但在此之前,它将标志设置为 true。
threadB
从
data
缓冲区读取。读取后,它会检查原子标志。
#include <atomic>
uint8_t data[4096] = {};
std::atomic<bool> flag = false;
void threadA() {
flag.store(true, std::memory_order_relaxed);
std::atomic_thread_fence(std::memory_order_release);
for (uint8_t &byte : data) {
byte = 1;
}
}
void threadB() {
bool found_non_zero = false;
for (uint8_t byte : data) {
if (byte) {
found_non_zero = true;
}
}
std::atomic_thread_fence(std::memory_order_acquire);
bool flag_observed = flag.load(std::memory_order_relaxed);
if (found_non_zero) {
ASSERT_TRUE(flag_observed);
}
}
是否可以保证,如果threadB
从缓冲区读取了任何非零数据,那么它必须观察到该标志被设置为true(即断言总是成功)?希望
threadA
中的释放栅栏可以防止将
flag.store
重新排序到写入
data
之后,并且
threadB
中的获取栅栏可以防止将
flag.load
重新排序到读取
data
之前.但是,根据栅栏到栅栏同步的
标准,获取栅栏仅在原子加载之后出现时才有效;同样,释放栅栏仅在出现在原子存储之前时才有效。这表明 data
应该是原子的,而标志不应该是原子的,这似乎是错误的。如果栅栏确实没有效果,那么对代码进行哪些修改才能确保断言始终成功?
栅栏确保如果您在原子上看到一个观察(数据为 1),那么栅栏(标志)之后的其他非原子观察将至少与原子(数据)一样新。
你的代码有相反的情况。