在WinAPI线程之间使用volatile作为共享变量

时间:2015-06-19 10:22:23

标签: c++ multithreading winapi

C ++编译器对线程一无所知,C ++编译器只将“线程”看作一个函数。

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

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

因此,如果我创建全局变量volatile,那么这将告诉C ++编译器不要优化变量的访问代码,并且总是直接访问内存位置。

这是对的吗?

1 个答案:

答案 0 :(得分:1)

是。不,也许。

在现代,标准的跨平台c ++ volatile既不是必要也不是足够来实现你正在做的事情。只是因为你告诉编译器不要优化读取,你不要告诉cpu /内存不应该重新排序读/写。而且标准还说,在某些地方读书时你也可能正在写它是 undefined behavoir 。你可能会因为它们不合理的强大内存模型而在x86 / x64上侥幸逃脱,但是你不应该承担风险。 volatile用于驱动程序和操作系统与硬件通信。

您应该使用现代的方法来实现这一点 std::atomic<...>。您可以安全地同时读取和写入std::atomic,并且读取/写入不会被优化掉*。它只是正确的选择。

但是......如果您专门为Windows编写并使用Visual Studio的风格,那么当使用volatile时,该编译器会提供额外的保证,这将在您的情况下使用正确的编译器选项。 VS将volatile的保证强度提高到类似于C#&amp; Java使用volatile。这意味着在你的情况下它会起作用。在最新版本的VS上,您可以通过/volatile编译器选项来控制此行为。但是,除非必要,否则我不建议这样做。除非你没有选择,否则使用标准的std :: atomic。

请注意,C ++ / Java和VS的volatile扩展名实际上比使用std::atomic弱。 std::atomic保证顺序包含,Java和C#都不能表示Dekkers algorithm可以与std :: atomic一起使用。

* std::atomic实际上可以进行优化,但前提是您无法在as-if规则下注意到。这是另一个原因,它们比volatile更好地打破了优化,因为它必须完成,即使不必要,因为编译器无法进行推理。