我有两个问题——考虑两个线程,一个对共享数据进行更改,另一个对该共享数据进行操作。在对数据执行任何操作之前,两个线程都会获取一个互斥对象。
我如何保证对数据进行操作的线程总是看到第一个线程所做的更改?是否需要一组获取/释放围栏,或者线程是否通过使用互斥来隐式同步?如果我不使用互斥(但以其他方式确保独占访问),该怎么办?
而且:如果没有交错/后续原子操作(比如,将标志存储在atomic_bool中,发出"就绪"信号或其他什么),fence真的能做什么吗?
这里有一个用例:
void func()
{
std::atomic_bool quit = false;
std::vector<float> data(100);
std::mutex m;
std::thread one([&]()
{
while (!quit.load(std::memory_order_relaxed))
{
std::unique_lock<std::mutex> lock(m, std::try_to_lock);
if (lock.owns_lock())
{
for (int i = 0; i < data.size(); ++i)
{
data[i] = std::rand();
}
std::atomic_thread_fence(std::memory_order::memory_order_release);
}
}
}
);
std::thread two([&]()
{
while (!quit.load(std::memory_order_relaxed))
{
std::unique_lock<std::mutex> lock(m, std::try_to_lock);
if (lock.owns_lock())
{
// guaranteed that any changes from thread one to 'data' is seen after this fence?
std::atomic_thread_fence(std::memory_order::memory_order_acquire);
auto res = std::accumulate(data.begin(), data.end(), 0);
std::cout << "Accumulated result is: " << res << std::endl;
}
}
}
);
fgetc(stdin);
quit.store(true);
one.join(); two.join();
}
我如何保证对数据进行操作的线程总是看到第一个线程所做的更改?是否需要一组获取/释放围栏,或者线程是否通过使用互斥来隐式同步?
Mutex做所有的工作。不需要额外的内存围栏。
如果我不使用互斥(但以其他方式确保独占访问),该怎么办?
这取决于确保exclusive access
的机制。大多数机械装置不需要额外的围栏。[严格来说,独占访问定义意味着对应的内存排序。]
当访问实际上是并发时,可能需要内存围栏,但当访问其他变量时,您希望确保观察到一些不变量。
fence在没有交错/后续原子操作的情况下真的能做任何事情吗(比如,在atomic_bool中存储一个标志,发出"就绪"之类的信号)?
围栏不仅仅局限于原子操作。实际上,围栏只是屏障,它将所有访问(存储、加载或两者兼有,取决于围栏的类型)分为两组:围栏之前和之后。
Fence只是硬件使内部缓存与主内存同步的信号。实际上,它们是执行障碍,在通过这个障碍之前需要内存同步。因此得名。