在WinAPI线程之间的共享变量中使用volatile [英] Using volatile for a shared variable between WinAPI threads

查看:82
本文介绍了在WinAPI线程之间的共享变量中使用volatile的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

C ++编译器对线程一无所知,C ++编译器仅将线程"视为函数.

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

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

因此,如果我将全局变量设置为volatile,则这将告诉C ++编译器不要优化该变量的访问代码,并且始终直接访问内存位置.

这正确吗?

解决方案

是.不,也许.

在现代的标准跨平台c ++中,volatile既不是实现必需的功能,也不是必需的.只是因为您告诉编译器不要优化读取,所以您不告诉CPU/内存不应对读取/写入进行重新排序.该标准还规定,在您可能还在写信的同时从某处阅读是未定义的行为.由于x86/x64的内存模型过强,您可能会放弃它,但是您不应该冒险. volatile用于驱动程序和操作系统与硬件对话.

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

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

请注意,C ++/Java和VS的volatile扩展实际上比使用std::atomic弱. std::atomic的保证顺序一致性,Java和C#都没有,这意味着 Dekkers算法可以与std :: atomic一起使用.

实际上,

* std::atomic可以被优化,但前提是您不能根据准入规则注意到这一点.这是它们优于volatile的另一个原因,因为它破坏了优化,因为即使没有必要,编译器也无法进行推理,即使必须这样做也必须这样做.

The C++ compiler does not know anything about threads, the C++ compiler sees a "thread" simply as a function.

Now say I have two threads/functions, and I have one global variable.

If I am accessing the global variable in these two threads/functions, the C++ compiler could optimize the variable access code in these two threads and copy the global variable into a register and start manipulating the register and not the memory location. Now since each thread have a unique set of registers, if these two threads are running simultaneously, then they will not be accessing the global variable in memory, but rather each thread is manipulating its own register!

So if I make the global variable volatile, then this will tell the C++ compiler not to optimize the access code for the variable, and always access the memory location directly.

Is this correct?

解决方案

Yes. No. Maybe.

In modern, standard, cross platform c++ volatile is neither necessary nor sufficient to achieve what you are doing. Just because you tell the compiler not to optimize out the read you do not tell the cpu/memory that the read/write shouldn't be re-ordered. And the standard also says that reading from somewhere whilst you may also be writing to it is undefined behavoir. You may get away with it on x86/x64 due to their unreasonably strong memory models but you shouldn't take the risk. volatile is for drivers and os's to talk to hardware.

You should use the modern way of doing this which is using a std::atomic<...>. You can safetly read and write simultaneously to a std::atomic and reads/writes will not be optimized away*. Its simply the right choice.

However... if you are writing exclusively for Windows and using a flavor of Visual Studio then that compiler gives additional guarantees when using volatile that will work in your case with the right compiler options. VS increases the strength of the guarantee for volatile to similar to what C# & Java uses volatile for. It means in your case it will work. On the latest version of VS you can control this behavoir through /volatile compiler option. HOWEVER, I do not recommend doing this unless its necessary. Use the standard std::atomic's unless you dont have a choice.

Note that C++/Java and VS's extension for volatile is actually weaker than using std::atomic. std::atomic's guarantee sequential consistancy which neither Java nor C# do which means Dekkers algorithm can be used with std::atomic.

*std::atomic's actually can be optimised out but only if you couldn't possibly notice under the as-if rule. This is another reason they're better than volatile which break optimizations as it simply MUST be done even if unnecessary as its impossible for the compiler to reason about.

这篇关于在WinAPI线程之间的共享变量中使用volatile的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆