对 WinAPI 线程之间的共享变量使用易失性
Using volatile for a shared variable between WinAPI threads
线程一无所知,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
更好的另一个原因,因为即使没有必要,编译器也无法推理,也必须这样做。
- 易失性sig_atomic_t的内存安全性
- 当 2 个线程共享同一物理内核时,具有错误共享的易失性增量在发布中的运行速度比在调试中慢
- 是否允许编译器优化掉局部易失性变量
- 访问共享内存而不使用易失性、std::atomic、信号量、互斥锁和自旋锁
- 兼容的声明 __attribute__ ((节( ".abc.dfe" ))) 常量易失性 uint8 属性变量 = 0;- 符合MISRA标准
- 常量 & 指非易失性变量。变量将更改。更改是否使常量 & 无效?
- 如何使用 LLVM 传递使变量易失性或常量
- 优化易失性堆栈变量的存储/构造是否合法
- C++变量定义中的易失性类型量词位置
- 非易失性变量
- 如何使用易失性与2D共享内存
- 在 C++ 中易变:我是否应该定义一个可能被几个线程更改的变量作为易失性
- 使用易失性布尔变量忙于等待
- 具有易失性原子变量的原子操作
- 如果我锁定变量,我是否必须将复杂类型标记为“易失性”?
- 为什么这个易失性变量的地址总是在1
- c++编译器能否消除未读的易失性局部变量?
- 在C中递增一个易失性变量
- GCC可以使用读-修改-写指令来更新易失性变量吗?
- 对 WinAPI 线程之间的共享变量使用易失性