一个线程写入的值是否保证可以被另一个线程看到,而无需锁定该变量?

Loc*_*yer 5 c++ multithreading x86-64

我试图了解 x86-64 机器上 C++ 的一致性保证,以及在其他平台上是否有所不同。具体来说,我想知道是否可以保证如果一个线程在下一个线程读取变量之前写入变量,我是否总是会看到正确的值?我读过一些关于此的相互矛盾的信息。

\n\n

这是一个示例,其中Shared在另一个线程中更新,并且仅当我们确定值已完成更新时才读取结果。这总是打印 1 吗?我每次测试的时候都是如此,但我无法通过例子来证明这一点。

\n\n
#include <iostream>\n#include <thread>\n#include <atomic>\n\nstd::atomic<bool> Done {false};\nint Shared = 0;\n\nint main(int argc, const char* argv[])\n{\n    auto Thread = std::thread([](){\n        Shared = 1;\n        Done = true;\n    });\n\n    while(!Done)\n    {\n    }\n\n    std::cout << Shared << std::endl;\n\n    Thread.join();\n    return 0;\n}\n\n
Run Code Online (Sandbox Code Playgroud)\n\n

该博客似乎建议在 x86-64 上所有写入对其他线程可见:http://benbowen.blog/post/cmmics_iii/

\n\n

这份文件听起来也有类似的内容:

\n\n
\n

当线程写入共享内存位置时,MESIF 协议要求该内存位置的所有其他副本均无效。如果发生这种情况,处理器中的所有内核都会向 LLC(在本例中为 L3\xc2\xadcache)发送一个 RFO\xc2\xadrequest(读取\xc2\xadfor\xc2\xadownership 请求),以检查 snoop \xc2\xadfilter,然后将失效发送到所有保存缓存行副本的缓存\xe2\x80\x99。

\n
\n\n

https://www.eit.lth.se/fileadmin/eit/courses/edt621/Rapporter/2015/robin.skafte.pdf

\n

Jer*_*man 4

它很复杂,但在您的特定情况下,这保证可以在所有 C++ 实现中工作。

原因是操作的默认内存顺序std::atomicstd::memory_order_seq_cst,这确实保证了释放/获取语义。

具体来说,这意味着线程在释放存储(包括顺序一致的存储,例如您的存储)之前对内存进行的任何写入对于在同一原子上获取加载的任何线程(包括顺序一致的加载,例如您的存储)都是可见的,并且观察新值。

  • 当然有:它发生在 while 循环条件中获取读取到 `Done` 之后,因此无法重新读取 `Shared` 是一种不健全的优化,除非编译器可以证明没有线程可以在发布之前写入它-store 到 `Done` (因为这样的释放存储,正如这里存在的那样,会进行内存写入,包括那些对 `Shared` 的写入,在获取加载之后产生可见的副作用)。 (2认同)