指向堆栈变量的指针应该是易变的吗?
Should a pointer to stack variable be 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(true)
替换 while(!stop)
,因此改变 *p_stop
什么都不做。
那么,在这种情况下是否需要将指针标记为 volatile?
P.S:请不要教我为什么不使用这个,使用这个 hack 的真实代码是出于(复杂到无法解释的)原因这样做的。
编辑:
我没有提到这两个函数 运行 在不同的线程上。
worker()
是第一个线程的函数,应该使用 p_stop
指针从另一个线程停止。
我不想知道有什么更好的方法来解决这种黑客行为背后的真正原因。我只是想知道这是否是 C++ 中的 defined\undefined 行为(就此而言为 11),以及这是否是 compiler\platform\etc 相关的。到目前为止,我看到@Puppy 说每个人都错了,这是错误的,但没有引用表示这一点的特定标准。
我知道你们中的一些人对“不要教训我”的部分感到冒犯,但请坚持真正的问题——我应该使用 volatile
还是不使用?或者这是UB?如果可以,请提供完整的答案,帮助我(和其他人)学习新知识。
P.S: Please do not lecture me on why not to use this,
我不确定我们应该说什么。编译器管理堆栈,因此您使用它所做的任何事情在技术上都是未定义的行为,并且在您升级到下一个版本的编译器时可能无法正常工作。
您所做的假设也可能与编译器优化时的假设不同。这才是使用(或不使用)volatile
的真正原因;您为编译器提供指导,帮助它确定优化是否安全。 volatile
的使用告诉编译器它应该假设这些变量可能会由于外部影响(其他线程或特殊硬件行为)而改变。
所以是的,在这种情况下,您似乎需要用 volatile
限定符标记 p_stop
和 stop
.
(注意:这是必要但不充分的,因为它不会导致在具有宽松内存模型的语言实现中发生适当的行为,该模型需要障碍来确保正确性。参见 https://en.wikipedia.org/wiki/Memory_ordering#Runtime_memory_ordering )
So, is it required to mark the pointer as volatile in such a case?
没有。这不是必需的,主要是因为 volatile 在这种情况下甚至无法远程覆盖您需要它做的事情。您必须使用实际的同步原语,例如原子操作或互斥锁。在此处使用 volatile
是未定义的行为,您的程序将爆炸。
volatile
对并发没有用。它可能对实现并发原语有用,但还远远不够。
坦率地说,您是否要使用实际的同步原语是无关紧要的。如果你想写出正确的代码,你别无选择。
根据提供的详细信息根本无法回答这个问题。
如问题中所述,这是一种完全不受支持的线程间通信方式。
所以唯一的答案是:
指定您正在使用的编译器版本,并希望有人知道它最黑暗的秘密或参考您的文档。所有 C++ 标准都会告诉您这是行不通的,而且任何人都可以告诉您的是 "might work but don't".
没有 "oh, come on guys everyone knows it pretty much works what do I do as the workaround? wink wink" 答案。
除非您的编译器不支持原子或适当的并发机制,否则没有正当理由这样做。
"It's not supported" 不是 "complex-to-explain" 所以我会根据该代码片段着迷,以了解没有正确执行此操作的可能原因(除了古老的编译器)。
I simply want to know if this is defined\undefined behavior in C++ (11 for that matter)
Ta-da(来自 N3337、"quasi C++11")
Two expression evaluations conflict if one of them modifies a memory location [..] and the other one accesses or modifies the same memory location.
§1.10/4
和:
The execution of a program contains a data race if it contains two conflicting actions in different threads, at least one of which is not atomic, and neither happens before the other. Any such data race results in undefined behavior. [..]
§1.10/21
您正在从不同的线程访问对象 stop
的(内存位置),这两种访问都不是原子的,因此也没有 "happens before" 关系。简而言之,您有数据竞争,因此存在未定义的行为。
I am not interested in knowing what better ways there are to solve the real reason that is behind this sort of hack.
原子操作(由 C++ 标准定义)是唯一(可靠)解决此问题的方法。
我知道我应该使用 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(true)
替换 while(!stop)
,因此改变 *p_stop
什么都不做。
那么,在这种情况下是否需要将指针标记为 volatile?
P.S:请不要教我为什么不使用这个,使用这个 hack 的真实代码是出于(复杂到无法解释的)原因这样做的。
编辑:
我没有提到这两个函数 运行 在不同的线程上。
worker()
是第一个线程的函数,应该使用p_stop
指针从另一个线程停止。我不想知道有什么更好的方法来解决这种黑客行为背后的真正原因。我只是想知道这是否是 C++ 中的 defined\undefined 行为(就此而言为 11),以及这是否是 compiler\platform\etc 相关的。到目前为止,我看到@Puppy 说每个人都错了,这是错误的,但没有引用表示这一点的特定标准。
我知道你们中的一些人对“不要教训我”的部分感到冒犯,但请坚持真正的问题——我应该使用 volatile
还是不使用?或者这是UB?如果可以,请提供完整的答案,帮助我(和其他人)学习新知识。
P.S: Please do not lecture me on why not to use this,
我不确定我们应该说什么。编译器管理堆栈,因此您使用它所做的任何事情在技术上都是未定义的行为,并且在您升级到下一个版本的编译器时可能无法正常工作。
您所做的假设也可能与编译器优化时的假设不同。这才是使用(或不使用)volatile
的真正原因;您为编译器提供指导,帮助它确定优化是否安全。 volatile
的使用告诉编译器它应该假设这些变量可能会由于外部影响(其他线程或特殊硬件行为)而改变。
所以是的,在这种情况下,您似乎需要用 volatile
限定符标记 p_stop
和 stop
.
(注意:这是必要但不充分的,因为它不会导致在具有宽松内存模型的语言实现中发生适当的行为,该模型需要障碍来确保正确性。参见 https://en.wikipedia.org/wiki/Memory_ordering#Runtime_memory_ordering )
So, is it required to mark the pointer as volatile in such a case?
没有。这不是必需的,主要是因为 volatile 在这种情况下甚至无法远程覆盖您需要它做的事情。您必须使用实际的同步原语,例如原子操作或互斥锁。在此处使用 volatile
是未定义的行为,您的程序将爆炸。
volatile
对并发没有用。它可能对实现并发原语有用,但还远远不够。
坦率地说,您是否要使用实际的同步原语是无关紧要的。如果你想写出正确的代码,你别无选择。
根据提供的详细信息根本无法回答这个问题。
如问题中所述,这是一种完全不受支持的线程间通信方式。
所以唯一的答案是:
指定您正在使用的编译器版本,并希望有人知道它最黑暗的秘密或参考您的文档。所有 C++ 标准都会告诉您这是行不通的,而且任何人都可以告诉您的是 "might work but don't".
没有 "oh, come on guys everyone knows it pretty much works what do I do as the workaround? wink wink" 答案。
除非您的编译器不支持原子或适当的并发机制,否则没有正当理由这样做。 "It's not supported" 不是 "complex-to-explain" 所以我会根据该代码片段着迷,以了解没有正确执行此操作的可能原因(除了古老的编译器)。
I simply want to know if this is defined\undefined behavior in C++ (11 for that matter)
Ta-da(来自 N3337、"quasi C++11")
Two expression evaluations conflict if one of them modifies a memory location [..] and the other one accesses or modifies the same memory location.
§1.10/4
和:
The execution of a program contains a data race if it contains two conflicting actions in different threads, at least one of which is not atomic, and neither happens before the other. Any such data race results in undefined behavior. [..]
§1.10/21
您正在从不同的线程访问对象 stop
的(内存位置),这两种访问都不是原子的,因此也没有 "happens before" 关系。简而言之,您有数据竞争,因此存在未定义的行为。
I am not interested in knowing what better ways there are to solve the real reason that is behind this sort of hack.
原子操作(由 C++ 标准定义)是唯一(可靠)解决此问题的方法。