Interleaving
依然,我不太清楚这个术语Interleaving怎么翻译好,看到有前人翻译成顺序交错,我们就用这个译名好了。
顺序交错是指当有多个协程共同工作时,代码的执行顺序会有多种可能性。
并发发生在机器码等级
并发发生在机器码等级,而不是源代码等级。也就是说Interleaving并不是Go 源代码的Interleaving,而是底层的机器码指令的Interleaving。不管您的平台是什么,Go 源代码都会被编译成机器。这些机器码指令是一系列比源代码更小的指令,这些指令才是实际造成interleaving的源头。总之,每一条Go源代码指令,都可以被编译成一连串机器码指令。所以Interleving可以在一条Go源代码指令的执行阶段被拆分成数段或者说被中断。
一个例子
我们依然可以用i=i+1这个例子来进行说明。
i=i+1可以被看成三条机器码指令:
- read i
- increment
- write i
第一步是CPU先将i从存储单元里读取出i,然后我们需要知道的是我们的机器是基于寄存器的,所以这个increament 操作会把i写入一个寄存器,并进行算术运算,最后再把结果写回存储单元中。
因为并发是发生在机器码等级,所以当你在执行条源代码指令i=i+1的时候,i的值完全有可能被其它也操作i的协程改变。
如上图,假如有两个协程,都要执行i=i+1这条语句。只了解Go源代码的程序员对i的预期结果可能是2。但是我们通过把两条语句编译成机器码指令可以发现,因为两个读操作(1和2)可能是紧接着发生的两个指令,也就是说此时Task 1读到的i的值是0,Task2读到的i的值也是0,那两个任务分别对i进行自加1操作后,最后写回存储单元的i的值还是1,而不是预期的2。
最后的总结就是“Interleaving machine instructions causes unexpected problems" 代码执行顺序交错的机器指令会导致不可预料的结果。