当我查看golang内存模型文档(link)时,我发现了一个奇怪的行为。该文档说下面的代码可能发生g打印2然后0。
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 :(得分:7)
变量a
和b
被分配并初始化为其各自类型的零值(在0
的情况下为int
),然后才能启动任何函数执行,在这一行:
var a, b int
可能会更改的是f()
函数中为其分配新值的顺序。
从该页面引用:Happens Before:
在单个goroutine中,读取和写入必须表现得好像它们按程序指定的顺序执行。也就是说,只有当重新排序不改变语言规范定义的goroutine内的行为时,编译器和处理器才可以重新排序在单个goroutine中执行的读取和写入。由于这种重新排序,一个goroutine观察到的执行顺序可能与另一个goroutine所感知的顺序不同。例如,如果一个goroutine执行
a = 1; b = 2;
,则另一个goroutine可能会在b
的更新值之前观察到a
的更新值。
a
和b
的分配可能不会按照您编写它们的顺序进行,如果重新排序它们对同一个goroutine没有影响。例如,如果首先更改b
的值更有效(例如因为其地址已经加载到寄存器中),编译器可以对它们重新排序。如果更改分配顺序会(或可能)在同一个goroutine中引起问题,那么显然不允许编译器更改顺序。由于f()
函数的goroutine在赋值后对变量a
和b
没有任何作用,编译器可以按任何顺序自由执行赋值。
由于上例中的2个goroutine之间没有同步,编译器不会检查重新排序是否会导致其他goroutine出现任何问题。它没有。
如果您同步goroutine,编译器将确保在“同步点”不存在不一致性:您将保证在此时两个分配都将“完成”;因此,如果“同步点”位于print()
来电之前,那么您会看到已打印的指定新值:2
和1
。