指向堆栈变量的指针是否应该是易变的?

时间:2017-04-06 20:46:34

标签: c++ compiler-optimization volatile

我知道我应该使用volatile关键字告诉编译器不要优化内存read \ write到变量。我也知道在大多数情况下should only be used to talk to non-C++ memory

但是,我想知道在持有指向某个本地(堆栈)变量的指针时是否必须使用volatile

例如:

//global or member variable
/* volatile? */bool* p_stop;

void worker()
{
    /* volatile? */ bool stop = false;
    p_stop = &stop;
    while(!stop)
    {
        //Do some work
        //No usage of "stop" or p_stop" here
    }
}

void stop_worker()
{
    *p_stop = true;
}

在我看来,像某个优化级别的编译器可能会看到stop是一个局部变量,永远不会更改,可以用while(!stop)替换while(true),从而更改{ {1}}什么都不做。

那么,在这种情况下是否需要将指针标记为易失性?

P.S:请不要告诉我为什么不使用它,使用这个黑客的真正代码是为了(复杂到解释)的原因。

编辑:

  1. 我没有提到这两个函数在不同的线程上运行。 *p_stop是第一个线程的函数,应该使用worker()指针从另一个线程停止。

  2. 我不知道有什么更好的方法来解决这种黑客背后的真正原因。我只是想知道这是否是C ++中定义的\未定义的行为(11),如果这是依赖于编译器\ platform \ etc的话。到目前为止,我看到@Puppy说每个人都错了,这是错误的,但没有引用表示这个的特定标准。

  3. 我知道你们中有些人被“不要讲我”这一部分所冒犯,但请坚持真正的问题 - 我应该使用p_stop吗?或者是这个UB?如果你能提供完整的答案,请帮助我(和其他人)学习新的东西。

4 个答案:

答案 0 :(得分:7)

  

我只是想知道这是否是C ++中定义的\未定义的行为(就此而言为11)

Ta-da(来自N3337,“准C ++ 11”)

  

如果其中一个修改内存位置[..]而另一个访问或修改相同的内存位置,则两个表达式评估会发生冲突。

     

§1.10/ 4

  

程序的执行包含数据竞争,如果它在不同的线程中包含两个冲突的动作,其中至少有一个不是原子的,并且都不会在另一个之前发生。任何此类数据争用都会导致未定义的行为。 [..]

     

§1.10/ 21

您正在从不同的线程访问对象stop的(内存位置),这两个访问都不是原子的,因此也没有“之前发生”关系。简单地说,您有数据竞争因此未定义的行为

  

我不知道有什么更好的方法来解决这种黑客背后的真正原因。

原子操作(由C ++标准定义)是唯一方式(可靠地)解决此问题。

答案 1 :(得分:4)

  

那么,在这种情况下是否需要将指针标记为易失性?

没有。这不是必需的,主要是因为在这种情况下,volatile甚至无法远程覆盖您需要它做的事情。您必须使用实际的同步原语,如原子操作或互斥锁。在这里使用volatile是未定义的行为,您的程序将爆炸。

volatile对并发性没有用。它可能对实现并发原语有用,但远远不够。

坦率地说,您是否想要使用实际的同步原语是无关紧要的。如果您想编写正确的代码,您别无选择

答案 2 :(得分:2)

  P.S:请不要告诉我为什么不使用它,

我不确定我们应该说什么。编译器管理堆栈,因此您使用它执行的任何操作都是技术上未定义的行为,并且在升级到下一版本的编译器时可能无法正常工作。

在进行优化时,您也会做出与编译器假设不同的假设。这是使用(或不使用)volatile的真正原因;您为编译器提供指导,帮助它确定优化是否安全。 volatile的使用告诉编译器它应该假设这些变量可能由于外部影响(其他线程或特殊硬件行为)而改变。

所以是的,在这种情况下,您似乎需要使用p_stop限定符标记 stopvolatile

(注意:这是必要但不充分的,因为它不会导致在具有宽松内存模型的语言实现中发生适当的行为,这需要障碍以确保正确性。请参阅https://en.wikipedia.org/wiki/Memory_ordering#Runtime_memory_ordering

答案 3 :(得分:1)

这个问题根本无法从提供的细节中得到解答。

正如问题所述,这是一种完全不受支持的线程间通信方式。

所以唯一的答案是:

指定您正在使用的编译器版本,并希望有人知道其最黑暗的秘密或参考您的文档。所有的C ++标准都会告诉你这是不是很有效,所有人都可以告诉你,可能会有所作为,但不要这样做。

没有"哦,来吧,大家都知道它几乎可以解决我的工作方式吗? 眨眼 眨眼"回答。

除非您的编译器不支持原子或适当的并发机制,否则没有正当理由这样做。 "它不受支持"不是复杂的解释"所以我会基于该代码片段着迷,以了解不能正确执行此操作的可能原因(除了古代编译器之外)。