我正在学习不同的记忆顺序。
我有这段代码,它可以工作并通过 GCC 和 Clang 的线程消毒剂:
#include <atomic>
#include <iostream>
#include <future>
int state = 0;
std::atomic_int a = 0;
void foo(int from, int to)
{
for (int i = 0; i < 10; i++)
{
while (a.load(std::memory_order_acquire) != from) {}
state++;
a.store(to, std::memory_order_release);
}
}
int main()
{
auto x = std::async(std::launch::async, foo, 0, 1);
auto y = std::async(std::launch::async, foo, 1, 0);
}
我认为如果它最终没有返回from
,那么“获取”加载是不必要的,所以我决定使用“放松”加载,然后是“获取”围栏。
我希望它能够工作,但它被 thread sanitizers 拒绝,它声称 concurrentstate++
是数据竞争。
#include <atomic>
#include <iostream>
#include <future>
int state = 0;
std::atomic_int a = 0;
void foo(int from, int to)
{
for (int i = 0; i < 10; i++)
{
while (a.load(std::memory_order_relaxed) != from) {}
std::atomic_thread_fence(std::memory_order_acquire);
state++;
a.store(to, std::memory_order_release);
}
}
int main()
{
auto x = std::async(std::launch::async, foo, 0, 1);
auto y = std::async(std::launch::async, foo, 1, 0);
}
为什么这是一场数据竞赛?
原子栅同步
线程 A 中的原子释放操作 X 与线程 B 中的获取栅栏 F 同步,如果
- 存在原子读取 Y(具有任何内存顺序)
- Y 读取 X 写入的值(或以 X 为首的释放序列)
- Y 在线程 B 中排在 F 之前
在这种情况下,在线程 A 中的 X 之前排序的所有非原子和松弛原子存储将发生在 F 之后线程 B 中相同位置的所有非原子和松弛原子加载之前。
据我了解,所有条件都满足:
- “存在原子读取 Y(具有任何内存顺序)” - 检查:
a.load(std::memory_order_relaxed)
。 - “Y 读取 X 写入的值”——检查,它从 读取值
a.store(to, std::memory_order_release);
。 - “Y 在线程 B 中排在 F 之前”——检查。