在约翰内斯的有价值的答案之后编辑和改进我的问题
bool b = true;
volatile bool vb = true;
void f1() { }
void f2() { b = false; }
void(* volatile pf)() = &f1; //a volatile pointer to function
int main()
{
//different threads start here, some of which may change pf
while(b && vb)
{
pf();
}
}
所以,让我们暂时忘记同步。问题是b是否必须声明为volatile。我已经阅读了标准并且知道了易失性语义的正式定义(我甚至几乎理解它们,这个词几乎是关键)。但是在这里让我们有点不正式。如果编译器在循环中看到b无法更改,那么除非b是volatile,否则它可以优化它并假设它等于while(vb)
。问题是,在这种情况下,pf本身是易失性的,编译器允许假设b在循环中不会改变,即使b不是易失性的吗?
请不要提出解决这段代码风格的评论和答案,这不是一个现实世界的例子,这是一个实验性的理论问题。 评论和答案除了回答我的问题之外,还更详细地讨论了挥发性的语义,你认为我误解了这些语言是非常受欢迎的。
我希望我的问题很明确。 TIA
再次编辑:
那这个呢?
bool b = true;
volatile bool vb = true;
void f1() {}
void f2() {b = false;}
void (*pf) () = &f1;
#include <iosrteam>
int main()
{
//threads here
while(b && vb)
{
int x;
std::cin >> x;
if(x == 0)
pf = &f1;
else
pf = &f2;
pf();
}
}
两个程序之间是否存在主要差异。如果是,那有什么区别?
答案 0 :(得分:3)
问题是,在这种情况下,pf本身是易失性的,编译器允许假设b在循环中不会改变,即使b不是易失性的吗?
它不能,因为你说其他线程可能会更改pf
,如果b
被调用,则间接更改pf
。因此,虽然理论上不需要正常读取b
,但实际上它必须读取它以确定它是否应该短路(当b
变为false
时它必须不读{{1}另一次)。
回答第二部分
在这种情况下,vb
不再是易失性的,因此编译器可以摆脱它,看到pf
有一个空体,f1
设置f2
为假。它可以优化b
如下
main
回答旧版本
允许编译器优化循环的一个条件是循环不访问或修改任何易失性对象(参见n3126中的[stmt.iter] p5)。你这样做,所以它无法优化循环。在C ++ 03中,不允许编译器甚至优化该循环的非易失性版本(但编译器仍然这样做)。
请注意,能够优化它的另一个条件是循环不包含同步或原子操作。在多线程程序中,无论如何都应该存在。所以,即使你摆脱了int main()
{
// threads here (which you say can only change "vb")
while(vb)
{
int x;
std::cin >> x;
if(x != 0)
break;
}
}
,如果你的程序编码正确,我认为编译器不能完全优化它。
答案 1 :(得分:2)
在我所理解的情况下,在这种情况下,当前C ++标准中对volatile
的确切要求并非完全由标准定义,因为该标准并未真正处理多线程。它基本上是一个编译器提示。所以,相反,我将解决典型编译器中发生的事情。
首先,假设编译器独立编译您的函数,然后将它们链接在一起。在任何一个示例中,您都有一个循环,您在其中检查变量,并调用函数指针。在该函数的上下文中,编译器不知道该函数指针背后的函数是什么,因此它必须始终在调用后从内存中重新加载b
。因此,volatile
与此无关。
将它扩展到第一个实际情况,并允许编译器进行整个程序优化,因为pf
是易失性的,编译器仍然不知道它将指向什么(它甚至不能假设它是f1
或f2
!),因此同样不能对函数指针调用中未修改的内容做出任何假设 - 因此volatile
上的b
是仍然无关紧要。
你的第二个案例实际上更简单 - vb
其中是一个红鲱鱼。如果消除这种情况,您可以看到即使在完全单线程语义中,函数指针调用也可能会修改b
。您没有对未定义的行为做任何事情,因此程序必须在没有volatile
的情况下正常运行 - 请记住,如果您没有考虑外部线程调整的情况,volatile
是 - 运。因此,如果图片中没有vb
,则您可能不需要volatile
,并且很明显添加vb
不会改变任何内容。
因此,总结:在任何一种情况下,您都不需要volatile
。在存在差异的情况下,在第一种情况下,如果fp
不是易失性的,那么足够先进的编译器可能会优化b
,而在程序中的任何地方它甚至都不会变化在第二种情况下。在实践中,我不认为任何编译器会实际进行优化。
答案 2 :(得分:0)
volatile只会伤害你。
在您的情况下,您说这些变量可以被其他线程更改。阅读代码,这是我在看到volatile时的假设,所以从维护者的角度来看,这很好 - 它给了我额外的信息(这是真的)。
我不知道优化是否值得尝试挽救,因为你说这不是真正的代码,但如果它们不是那么没有任何理由不使用volatile。
当您认为导致行为不正确时不使用volatile,因为优化正在改变代码的含义。
我担心编写编译器的标准和行为的细节,因为这样的事情可能会改变,即使它们没有,你的代码也会改变(这可能会影响编译器) - 所以,除非你在寻找对这个特定代码的微优化改进,我只是让它不稳定。