所有写入完成后保持互斥锁

问题描述 投票:0回答:1

我有一个关于 C++ 中线程安全和互斥锁的问题。我知道,在最简单的层面上,应该在可以同时读取和写入同一内存的任何点上持有互斥锁。但是,我也知道每个核心都有自己的缓存,因此我将互斥锁想象为一种确保所有线程在任何时间点都“同意”数据状态的方法。

我不确定的是当所有写入操作完成后会发生什么。那么在不拥有互斥锁的情况下使用数据是否安全?

参见下面的示例。根据我的浅薄理解,感觉最后一行可能会产生未定义的行为,因为代码不知道线程中已完成的所有写入操作。 IE。如果主线程缓存中的 arr 看起来与工作线程缓存中的 arr 不同怎么办?

std::mutex mtx;
std::vector<int> arr;

std::list<std::thread> threads;
for (int i = 0 ; i < 10 ; ++i) {
   threads.push_back([&](){
      std::lock_guard<std::mutex> lock(mtx);
      // Change arr here.
   });
}
for (auto& thread : threads) {
   thread.join();
}
// Can I use arr here without owning the mutex lock?
std::cout << arr[i] << std::endl;

所以我的问题是上面的例子是否安全,如果不安全,我可以在不持有互斥锁的情况下使用 arr 吗?

c++ multithreading concurrency mutex
1个回答
0
投票

是的,此代码是安全的。

C++标准对

std::thread::join()
的描述包括:

*this
表示的线程的完成与(6.9.2)同步 对应成功
join()
返回。

这意味着,如果某些操作 X 发生在线程完成之前(在 C++ 内存模型的技术意义上),并且某些操作 Y 发生在

join()
返回之后,则 X 发生在 Y 之前。在您的程序中,这意味着各个线程中对
arr
的操作都将发生在
join()
之后的主线程中的访问之前。因此不存在数据竞争。

对于实现而言,这实际上意味着终止线程的清理代码在通知等待

join()
的线程之前必须包含释放屏障,并且
join()
的代码在收到该通知后必须包含获取屏障。在实践中,退出并等待线程的操作系统服务可能会自动提供必要的障碍。

© www.soinside.com 2019 - 2024. All rights reserved.