2015-10-22 63 views
4

当我看看golang内存模型文档(link)时,我发现了一个奇怪的行为。此文件说,下面的代码可能会发生g打印2,然后0.go lang中的同步不正确

var a, b int 

func f() { 
    a = 1 
    b = 2 
} 

func g() { 
    print(b) 
    print(a) 
} 

func main() { 
    go f() 
    g() 
} 

这是唯一的例行问题吗?因为我很好奇,为什么变量'b'的值赋值会在'a'之前发生?即使'a'和'b'的值赋值会在不同的线程中发生(不在主线程中),是否必须确保'a'应该在它自己的线程中的'b'之前被赋值?(因为赋值' a'先来,'b'来的再来)任何人都可以清楚地告诉我这个问题吗?

+0

据我所知:b的g值的可见度不是由f决定的。 _里面的f a先设定,然后b设定,而不是反过来。 – Volker

回答

7

变量ab被分配和初始化与它们各自类型的零个值(这是0int的情况下)之前的任何的功能开始执行,在这一行:

var a, b int 

什么可能会改变的是f()函数中新值分配给它们的顺序。

从该页面引用:Happens Before

在一个单一的goroutine,读取并因为如果他们在程序中指定的顺序执行写操作必须表现。也就是说,编译器和处理器可能会重新排序在单个goroutine中执行的读取和写入操作,只有当重新排序不会改变语言规范定义的goroutine内的行为时。由于这种重新排序,一个goroutine观察到的执行顺序可能与另一个看到的顺序不同。例如,如果一个例程执行a = 1; b = 2;,另一个例程可能会在更新后的值a之前观察更新后的值b

分配到ab可能不会在顺序发生你写他们,如果他们重新排序不会使在同一够程的差异。例如,如果第一次更改b的值更有效(例如,因为其地址已经加载到寄存器中),则编译器可以对它们重新排序。如果更改分配顺序将会(或可能)在同一个例程中导致问题,那么显然编译器不允许更改顺序。由于f()函数的goroutine在赋值之后对变量ab没有任何影响,因此编译器可以按任何顺序自由执行赋值。

由于上例中的2个例程之间没有同步,因此编译器不会检查重排序是否会导致其他goroutine中的任何问题。它不需要。

如果你同步你的goroutines,编译器会确保在“同步点”不会有任何不一致:你将保证在这一点上这两个任务都将“完成”;因此如果“同步点”位于print()呼叫之前,则会看到分配的新值:21

+0

谢谢icza。嗯..这是一个编译器和处理器的问题。 (我的意思是这不是问题,但其原因是编译器和处理器。) –

+1

@KyuntaeEthanKim这是一个优化_and_同步问题:你的goroutines不同步,所以你不能保证其他goroutine会观察到什么以及是否有更新的值。它可以打印'2'和'1',可以打印'2'和'0',可以打印'0'和'1',可以打印'0'和'0'。 – icza