对 WinAPI 线程之间的共享变量使用易失性

Using volatile for a shared variable between WinAPI threads

本文关键字:易失性 共享变量 WinAPI 线程 之间      更新时间:2023-10-16
C++编译器对

线程一无所知,C++编译器将"线程"简单地视为一个函数。

现在假设我有两个线程/函数,我有一个全局变量。

如果我正在这两个线程/函数中访问全局变量,C++编译器可以优化这两个线程中的变量访问代码,并将全局变量复制到寄存器中并开始操作寄存器而不是内存位置。现在,由于每个线程都有一组唯一的寄存器,如果这两个线程同时运行,那么它们将不会访问内存中的全局变量,而是每个线程都在操作自己的寄存器!

所以如果我把全局变量volatile,那么这将告诉C++编译器不要优化变量的访问代码,并始终直接访问内存位置。

这是对的吗?

是的。不。或。

在现代、标准的、跨平台的 c++ 中,volatile既不需要也不足以实现您正在做的事情。仅仅因为您告诉编译器不要优化读取,您就不会告诉 CPU/内存不应重新排序读/写。该标准还说,当你也可能写信时,从某个地方阅读是不确定的行为。您可能会在 x86/x64 上侥幸逃脱,因为它们的内存模型不合理,但您不应该冒险。 volatile是驱动程序和操作系统与硬件通信。

您应该使用现代的方式来执行此操作,即使用 std::atomic<...> .您可以安全地同时读取和写入std::atomic并且读/写不会优化*。它只是正确的选择。

然而。。。如果您专门针对Windows编写并使用Visual Studio的风格,则该编译器在使用volatile时提供了额外的保证,这些保证将在正确的编译器选项的情况下适用于您的情况。VS增加了volatile的保证强度,类似于C#和Java使用vol。这意味着在您的情况下它会起作用。在最新版本的 VS 上,您可以通过/volatile 编译器选项控制此行为。但是,除非有必要,否则我不建议这样做。使用标准的std::atomic,除非你别无选择。

请注意,C++/Java 和 VS 对易失性的扩展实际上比使用 std::atomic 弱。 std::atomic保证了Java和C#都不保证的顺序一致性,这意味着戴克斯算法可以与std::atomic一起使用。

* std::atomic 实际上可以优化,但前提是您不可能在 as-if 规则下注意到。这是它们比破坏优化的volatile更好的另一个原因,因为即使没有必要,编译器也无法推理,也必须这样做。