识别干扰的可能性

Identifying possibility of interference

在多任务环境中。如果任务具有表达式 y = x + x; 是否有可能在 x 的两次读取之间发生中断(任务切换)。

这里是针对类似 x86 架构的特定答案,与传统调度程序 (https://en.wikipedia.org/wiki/Completely_Fair_Scheduler) 一起使用。看看这个 asm 代码,由 gcc 为 x86 生成,优化设置为 -OS0。

https://godbolt.org/g/AfKTkF

int main(void) {
  int y;
  int x = 5;
  y = x + x;
}

转为

main:
        pushq   %rbp
        movq    %rsp, %rbp
        movl    , -4(%rbp)
        movl    -4(%rbp), %eax  #read x
        addl    %eax, %eax      #execute x + x
        movl    %eax, -8(%rbp)
        movl    [=11=], %eax
        popq    %rbp
        ret

如您所见,x 仅被读取一次,这意味着即使有任务切换,也不会对 x 进行第二次读取。


编辑: EOL 在评论中指出 x 确实可以读取两次,修改上面的代码会产生以下结果:

volatile x = 5;

强制编译器插入两个 movl 操作(而不是一个):

movl    -8(%rbp), %edx
movl    -8(%rbp), %eax

虽然这并没有使我的第一个猜测错误。编译器将优化变量的读取访问(如果它是简单类型)并删除第二次读取。 volatile 关键字强制编译器访问寄存器中的最新值。第一个代码不会强制读取 - 因此在线程切换和修改 x.

的情况下给出其他结果

在大多数情况下,编译器生成的代码只会读取 x 一次,因为优化是微不足道的。事实上,它甚至可能将运算转换为右移,相当于 x << 1。但是你不能保证,如果 x 被声明为 volatile 它必然会进行两次读取,然后在读取低阶和高阶单词之间是不间断的(反之亦然),并且在这种情况下,y 的分配也是可中断的。

另一个问题是,如果 x 不是原子数据类型(即无法在单个指令中读取),例如 16 位目标上的 32 位类型,那么即使它如果是单次读取,它可能是可中断的。

在任何一种情况下,如果 x 本身(或第二种情况下的 y)在上下文之间共享(在这种情况下也应声明为 volatile 所以需要两次读取),或者如果由于某种原因,分配的时间在某种程度上是关键的并且需要完全确定(不太可能)。

如果 x 是共享的,因此 volatile 是原子类型,这个例子 中的简单解决方案是写表达式 y = x << 1y = x * 2 以确保 x 只读一次。 x 不是原子的 - 您可能需要锁定调度程序或禁用中断(即使用 临界区 ),或者更有选择性地使用互斥锁保护访问。对于无法将表达式简化为对共享变量的单个引用的更复杂的表达式,只需将变量分配给非共享局部临时变量即可确保它只被读取一次。原子性问题依然存在