一尘不染

Go lang中的同步不正确

go

在查看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’之后出现)有人可以清楚地告诉我这个问题吗?


阅读 239

收藏
2020-07-02

共1个答案

一尘不染

在开始执行任何函数之前,在以下行中使用各自的类型(在中为)的零值来分配和初始化变量,a并在此行:b``0``int

var a, b int

可能更改的是在f()函数中将新值分配给它们的顺序。

从该页面引用:之前发生

在单个goroutine中,读取和写入的行为必须像它们按照程序指定的顺序执行一样。也就是说,仅当重新排序不会改变语言规范所定义的该goroutine中的行为时,编译器和处理器才可以对单个goroutine中执行的读取和写入进行重新排序。由于此重新排序,一个goroutine观察到的执行顺序可能与另一个goroutine察觉到的执行顺序不同。例如,如果执行一个goroutine,则另一个goroutine
a = 1; b = 2;可能会在的更新值b之前观察到的更新值a

如果对它们进行重新排序不会对同一goroutine
产生影响a,则b可以按照您编写它们的顺序进行分配,并且可能不会发生。例如,如果首先更改的值b更有效(例如,因为其地址已加载到寄存器中),则编译器可能会对其重新排序。如果更改分配顺序会(或可能)在同一goroutine中引起问题,则显然编译器不允许更改顺序。由于f()函数的goroutine对变量不执行任何操作,a并且b在赋值之后,编译器可以自由地以任何顺序执行赋值。

由于在上面的示例中这两个goroutine之间没有同步,因此编译器不费力气检查重新排序是否会在另一个goroutine中引起任何问题。不必。

另外,如果您对goroutine进行同步,则编译器将确保在“同步点”不会出现任何不一致之处:您将确保在这时两个分配都将“完成”;因此,如果“同步点”在print()调用之前,那么您将看到打印的已分配新值:21

2020-07-02