2014-02-19 55 views
3

我看到了一个代码示例演示中的问题C++ volatile member functions的答案volatile预选赛的使用情况,引用如下:在这种情况下挥发性限定词是否重要?

volatile int x; 

int DoSomething() { 
    x = 1; 
    DoSomeOtherStuff(); 
    return x+1; // Don't just return 2 because we stored a 1 in x. 
       // Check to get its current value 
} 

我不知道,如果volatile预选赛使得在上面的代码中任何区别。 x是一个全局变量,并且在x上的写入和读取之间存在函数调用,并且我们只读取一次x。编译器不应该在x上执行真正的读取(即使它不是volatile)?

我认为这是从下面的情况不同:

volatile int x; 

int DoSomething() { 
    x = 1; 
    while (x != 1) 
     break; 
} 

在这种情况下,我们反复x写入后立即读取x,所以volatile需要得到x向上的最新值由其他线程编写。

我对自己对这些代码示例的理解不是很有信心,如果我错了,请纠正我。

编辑(回复评论):对不起,我没有把我的问题弄清楚。至于第一个代码片段,我在质疑代码是否是可能的使用volatile(而不是保证使用易失性)的有效示例。我只是想知道,没有volatile,假设没有多线程或其他非平凡的东西,比如内存映射IO,是否保证DoSomeOtherStuff()x的任何可能更改都可以反映在return x+1中。因为如果保证在没有volatile的情况下工作,那么这个例子就与此相关,甚至不要提到volatile的平台相关性质,因为有些评论指出。但如果没有保证,那么恐怕我现有的一些代码可能无法按我的预期工作。
(我可能不应该把第二代码片段在所有。)

+1

C++标准保证基本一无所知'volatile'关键字。 (尝试搜索“volatile is uselesss”,如果您正在讨论便携式多线程编程,那么它就是这样)。因此,行为完全取决于平台。请添加标签和/或更新您的问题,以表明您的意思是哪个编译器。 – Nemo

+0

这是一个很好的解释,说明volatile对于多线程编程实际上是非常有用的:http://www.drdobbs.com/cpp/volatile-the-multithreaded-programmers-b/184403766 –

+0

@MikeC:That文章正在对C语言规范中找不到的多线程程序中'volatile'的语义做出假设。如果一个特定的编译器实现碰巧使volatile属性引入了一个内存屏障或者使得一个变量“线程安全”所需要的任何东西(不管*表示什么),那么这是一个不可移植的和实现定义的关键字用法。一个更好的文章指出人们正如Nemo所说:http://software.intel.com/zh-cn/blogs/2007/11/30/volatile-almost-useless-for-multi-threaded-programming/。 –

回答

0

编译器可以有关于DoSomeOtherStuff()功能的一些信息。例如:

static int DoSomeOtherStuff() 
{ 
    return 42; 
} 

volatile int x; 

int DoSomething() { 
    x = 1; 
    DoSomeOtherStuff(); 
    return x+1; // Don't just return 2 because we stored a 1 in x. 
    // Check to get its current value 
} 

GCC与-O3选项完全删除DoSomeOtherStuff()功能(和它的身体也一样)的电话,但仍然重新加载x到底返回x+1

0

这里是关于挥发性现有的SO问题: What is the use of volatile keyword?

只需第一代码片段,你是对的,没有明显的理由,X声明的波动。但是,如果您了解关键字应该用于您的情况,可能会推断x是易变的,因为此代码之外有某些内容可能会改变它的值。例如,内存可能连接到其他硬件或被其他程序写入。因此,程序员正在指示编译器不能看到x的值改变的所有可能的方式。所以编译器可能无法以某些方式优化代码。

第二个代码片段本身并不需要volatile关键字。易失性用作编译器提示,指出内存可能会因当前程序之外的力而改变。它不应该用于线程通信。有些新的C++类型应该用于这些情况。

我推荐读Herb Sutter的this article以及this talk

0

真正确定的唯一方法就是检查为目标平台生成的汇编代码。如果volatile按照你的意图执行,读取变量x将通过内存加载完成。

+0

从什么内存?在许多机器上,读取等都是流水线操作,因此即使编译器生成加载指令,没有特殊的fence或membar指令,硬件也可能不会执行外部读取。 –

3

volatile限定符在代码本身的含义 中决不会产生差异。除非编译器可以证明 DoSomeOtherStuff()不修改x,否则必须重新读取x 不管,volatile或不。对于volatile是相关的, x将不得不像内存映射IO,其中 可能会改变程序之外。如果我们想象它是 这是每递增微秒寄存器,例如:

int 
MeasureExecutionTime() 
{ 
    x = 0; 
    DoSomeOtherStuff(); 
    return x; 
} 

将返回在DoSomeOtherStuff使用时间量; 编译器将被要求重新加载它,即使它内联 DoSomeOtherStuff,并且发现它从未修改过x

当然,在典型的桌面计算机上,可能没有任何内存映射IO,如果存在,则位于受保护的内存中,您不能访问它。而且很多编译器不会真正生成使其正常工作所需的代码。 因此,对于这类机器上的通用应用,在每次使用volatile时都有 确实没有意义。

编辑:

关于你的第二个代码片段:如通常实现的, volatile并不能保证你得到的 x向上最新副本。可以说,这不符合 volatile的意图,但它是g ++,Sun CC和至少一些VC++版本的工作方式。编译器会在循环中每次发出一个加载 的指令来读取x,但是硬件 可能会发现管道中已有的值,并且不会将请求读取到内存总线的 。为了保证 有新的读取,编译器将不得不插入一个fence指令或一个membar指令。

也许更重要(因为迟早会有些 发生,所以该值将不会在管道),这个循环 机制将被用于等待,直到写在 其他线程其他值已趋于稳定。除了挥发性对其他变量的读取和写入发生时没有 影响。

要理解volatile,了解 引入的意图很重要。当它被引入时, 内存管道等是未知的,并且(C)标准 忽略线程(或共享内存的多个进程)。 volatile的意图是允许支持映射内存 IO,其中写入地址具有外部结果,并且连续读取不会总是读取相同的内容。从来没有任何意图表示代码中的其他变量将会同步到 。当在线程之间进行通信时,通常将 的命令的读写写入到所有共享变量中,其中 非常重要。如果我这样做:

globalPointer = new Xxx; 

和其他线程可以访问globalPointer,这一点很重要 所有在Xxx构造函数中写入已成为 可见的globalPointer 出现值的变化之前。为实现这一目标,不仅将globalPointer有 是volatile,而且所有的Xxx成员,以及Xxx可以使用任何 变量成员函数,或任何数据 通过Xxx指针访问的。这根本不是 合理;你会很快结束 计划volatile中的所有内容。即使如此,它也将要求 编译器实现volatile正确,isserting围绕每个接入围栏或 membar指令。 (FWIW:围栏或 一个membar指令可以繁殖花费的时间用于通过的10倍或更多的存储器 存取)

这里的溶液是不挥发的,而是使接入到 指针(和只访问指针)原子,使用添加到C++ 11中的基元添加了 atomic_loadatomic_store。 这些原语确实导致使用必要的围栏或要素指令 ;他们还告诉编译器不要移动 任何内存访问。因此使用atomic_load到 设置指针,上方,将导致所有前述存储器写 为对其他线程是可见的在写指针 变得可见之前(条件是读出线程使用 atomic_read,当然— atomic_write的确保所有 先前的写入在所有 线程和“atomic_read”的“公共”存储器中可用,确保所有后续读取 将进入“公用”存储器,并且不会在管线中获取某个值 ) 。

0

volatile关键字是一种限定符,它可以防止来自编译器优化的对象,并告知编译器,在任何时候都可以更改对象的值,而不需要代码执行任何操作。它可以防止缓存中的变量进入寄存器,并确保每个访问变量都从内存中获取。

volatile关键字主要用于我们直接与GPIO处理,中断或标志注册。它也用于线程之间共享全局变量或缓冲区的地方。

适当的地方使用volatile关键字

变量应声明挥发时,其值可以意外更改。在实践中,必须每当你将变量声明为挥发性:

Accessing the memory-mapped peripherals register. 

    Sharing the global variables or buffers between the multiple threads. 

    Accessing the global variables in an interrupt routine or signal handler. 

这个环节也是有帮助的: http://aticleworld.com/understanding-volatile-qualifier-in-c/