Home

Go内存模型1

12 Dec 2012 by LelouchHe

例子

看看如下的代码有怎么样的输出

package main

import "fmt"

var a int
var b int

func main() {
    go func() {
        a = 1
        b = 2
    }()

    fmt.Println(a)
    fmt.Println(b)
}

答案

其实正确的答案应该是不确定,比如在我的机子上打印出来的是”0 2”,别的自己上等我回去验证下.

解释

其实这样说明个什么道理呢?在一个goroutine内部,我们可以严格的保证顺序,比如在上面那个闭包里,如果我们还在赋值之后使用a,那么a的值必然是1,因为在单一的goroutine中是顺序的,并没有并发之类的东西.然而在多个goroutine之间,这种顺序性是无法保证的,比如上面的a和b,两次赋值和两次读取,所以就无法保证执行的顺序,在我的机子上,就先执行了b = 2,然后才执行了a = 1

我们可以验证下单一goroutine的情况

package main

import (
    "fmt"
    "time"
)

var a int
var b int

func main() {
    go func() {
        a = 1
        b = 2
        fmt.Println(a)
        fmt.Println(b)
    }()

    time.Sleep(1)
}

在这种情况下,只能打印出”1 2”.在举个例子加深下印象

package main

import "fmt"

var a int
var b int

func main() {
    go func() {
        a = 1
        b = a + 1
    }()

    fmt.Println(a)
    fmt.Println(b)
}

能猜到答案么?呵呵,还是不确定.在我的机子上,打印出来的还是”0 2”,虽然b非常的依赖于a,但在外部的goroutine眼中,这些都是浮云.

结论

这个例子告诉我们一个道理,即Go的内存模型不能保证不同goroutine之间的顺序性,这种顺序性只能保证存于单一的goroutine中.在我们的并发编程中,我们不能依赖于其他goroutine的顺序来做出判断,就如此例一样,如果有这样的想法,必然带来问题.

解决之道就是Go的那句格言:”不要通过共享内存来通信,而要通过通信来共享内存”

其实说的就是上面这个例子,而这只是Go内存模型的一个小点.