在多任务环境中。如果任务具有表达式y = x + x;
,则在两次读取x之间可能发生中断(任务切换)。
答案 0 :(得分:1)
在大多数情况下,编译器生成的代码只会读取x一次,因为优化很简单。事实上,它甚至可能将操作转换为右移,相当于x << 1
。但是你不能保证,如果x
被声明为volatile
,它必然会使两次读取在读取低阶和高阶词之间不会中断(反之亦然),在这种情况下,y
的分配也是可以中断的。
另一个问题是,如果x
不是原子数据类型(即无法在单个指令中读取),例如16位目标上的32位类型,那么即使它是一个单读,可能是可中断的。
在任何一种情况下,如果x
本身(或第二种情况下的y
)在上下文之间共享,通常只会出现问题(在这种情况下,它也应该声明为volatile
所以两次读取是必要的),或者如果由于某种原因,分配的时间在某种程度上是关键的,并且需要完全确定(不太可能)。
如果x
是共享的,因此是volatile
但是是原子类型,此示例中的简单解决方案是编写表达式y = x << 1
或{{ 1}}确保y = x * 2
只读一次。其中x
不是原子的 - 您可能需要锁定调度程序或禁用中断(即使用关键部分),或者更有选择地使用互斥锁保护访问权限。对于表达式不能简化为对共享变量的单个引用的更复杂的表达式,只需将变量分配给非共享的本地临时变量,即可确保只读取一次。原子性问题仍然存在。
答案 1 :(得分:-1)
这里是x86架构的特定答案,与传统的调度程序(https://en.wikipedia.org/wiki/Completely_Fair_Scheduler)一起使用。看看这个asm代码,由gcc为x86生成,优化设置为-OS0。
int main(void) {
int y;
int x = 5;
y = x + x;
}
转向
main:
pushq %rbp
movq %rsp, %rbp
movl $5, -4(%rbp)
movl -4(%rbp), %eax #read x
addl %eax, %eax #execute x + x
movl %eax, -8(%rbp)
movl $0, %eax
popq %rbp
ret
正如你所看到的,x只被读取一次,因此暗示即使有任务切换,也不会有第二次读取。
修改强>
EOL在评论中指出x
确实可以读取两次,修改上面的代码产生如下:
volatile x = 5;
强制编译器插入两个movl操作(而不是一个):
movl -8(%rbp), %edx
movl -8(%rbp), %eax
这不会让我的第一次猜错。编译器将优化变量的读访问(如果它是简单类型)并删除第二次读取。 volatile关键字强制编译器访问寄存器中的最新值。第一个代码不会强制读取 - 因此在线程切换和修改x
时会产生其他结果。