我知道我应该使用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:请不要告诉我为什么不使用它,使用这个黑客的真正代码是为了(复杂到解释)的原因。
编辑:
我没有提到这两个函数在不同的线程上运行。
*p_stop
是第一个线程的函数,应该使用worker()
指针从另一个线程停止。
我不知道有什么更好的方法来解决这种黑客背后的真正原因。我只是想知道这是否是C ++中定义的\未定义的行为(11),如果这是依赖于编译器\ platform \ etc的话。到目前为止,我看到@Puppy说每个人都错了,这是错误的,但没有引用表示这个的特定标准。
我知道你们中有些人被“不要讲我”这一部分所冒犯,但请坚持真正的问题 - 我应该使用p_stop
吗?或者是这个UB?如果你能提供完整的答案,请帮助我(和其他人)学习新的东西。
答案 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
限定符标记 stop
和volatile
。
(注意:这是必要但不充分的,因为它不会导致在具有宽松内存模型的语言实现中发生适当的行为,这需要障碍以确保正确性。请参阅https://en.wikipedia.org/wiki/Memory_ordering#Runtime_memory_ordering)
答案 3 :(得分:1)
这个问题根本无法从提供的细节中得到解答。
正如问题所述,这是一种完全不受支持的线程间通信方式。
所以唯一的答案是:
指定您正在使用的编译器版本,并希望有人知道其最黑暗的秘密或参考您的文档。所有的C ++标准都会告诉你这是不是很有效,所有人都可以告诉你,可能会有所作为,但不要这样做。
没有"哦,来吧,大家都知道它几乎可以解决我的工作方式吗? 眨眼 眨眼"回答。
除非您的编译器不支持原子或适当的并发机制,否则没有正当理由这样做。 "它不受支持"不是复杂的解释"所以我会基于该代码片段着迷,以了解不能正确执行此操作的可能原因(除了古代编译器之外)。