在查看golang内存模型文档(link)时,我发现go lang上有一个奇怪的行为。该文档说,下面的代码可能会发生,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'之后出现)有人可以清楚地告诉我这个问题吗?
最佳答案
在开始执行任何功能之前,在以下行中分配变量a
和b
并使用其各自类型的零值(对于0
而言,为int
)进行初始化,在此行:
var a, b int
可能会改变的是
f()
函数中将新值分配给它们的顺序。从该页面引用:Happens Before:
如果重新排序
a
和b
在同一个goroutine中没有区别,则可能不会按照您编写它们的顺序进行分配。例如,如果首先更改b
的值更有效(例如,因为其地址已加载到寄存器中),则编译器可能会对其重新排序。如果在同一goroutine中更改分配顺序会(或可能)引起问题,那么显然不允许编译器更改顺序。由于f()
函数的goroutine在赋值后对变量a
和b
不执行任何操作,因此编译器可以自由执行任意顺序的赋值。由于在上面的示例中这两个goroutine之间没有同步,因此编译器不费力气检查重新排序是否会在另一个goroutine中引起任何问题。不必。
另外,如果您对goroutine进行同步,则编译器将确保在“同步点”不会出现任何不一致之处:您将确保在这时两个分配都将“完成”;因此,如果“同步点”在
print()
调用之前,那么您将看到打印的已分配新值:2
和1
。