Go的内存模型
转载请注明出处,原文链接http://tailnode.tk/2017/01/Go... 说明翻译自The Go Memory Model 介绍如何保证在一个goroutine中看到在另一个goroutine修改的变量的值,这篇文章进行了详细说明。 建议如果程序中修改数据时有其他goroutine同时读取,那么必须将读取串行化。为了串行化访问,请使用channel或其他同步原语,例如sync和sync/atomic来保护数据。 先行发生在一个gouroutine中,读和写一定是按照程序中的顺序执行的。即编译器和处理器只有在不会改变这个goroutine的行为时才可能修改读和写的执行顺序。由于重排,不同的goroutine可能会看到不同的执行顺序。例如,一个goroutine执行
为了保证对变量v的读操作r看到对v的写操作w,要确保w是r允许看到的唯一写操作。即当下面条件满足时,r 被保证看到w:
单独的goroutine中没有并发,所以上面两个定义是相同的:读操作r看到最近一次的写操作w写入v的值。当多个goroutine访问共享变量v时,它们必须使用同步事件来建立先行发生这一条件来保证读操作能看到需要的写操作。 同步初始化程序的初始化在单独的goroutine中进行,但这个goroutine可能会创建出并发执行的其他goroutine。 创建goroutine
var a string func f() { print(a) } func hello() { a = "hello,world" go f() } 调用 销毁goroutinegouroutine的退出并不会保证先行发生于程序的任何事件。例如下面程序: var a string func hello() { go func() { a = "hello" }() print(a) } 没有用任何同步操作限制对a的赋值,所以并不能保证其他goroutine能看到a的变化。实际上,一个激进的编译器可能会删掉整个go语句。 channel通信channel通信是goroutine同步的主要方法。每一个在特定channel的发送操作都会匹配到通常在另一个goroutine执行的接收操作。 var c = make(chan int,10) var a string func f() { a = "hello,world" c <- 0 } func main() { go f() <-c print(a) } 这个程序能保证打印出"hello,world"。对a的写先行发生于在c上的发送,先行发生于在c上的对应的接收完成,先行发生于 var c = make(chan int) var a string func f() { a = "hello,world" <-c } func main() { go f() c <- 0 print(a) } 此程序也能保证打印出"hello,world"。对a的写先行发生于从c接收,先行发生于向c发送完成,先行发生于 如果是带缓冲的channel(例如 var limit = make(chan int,3) func main() { for _,w := range work { go func(w func()) { limit <- 1 w() <-limit }(w) } select{} } 锁sync包实现了两个锁的数据类型sync.Mutex和sync.RWMutex。 var l sync.Mutex var a string func f() { a = "hello,world" l.Unlock() } func main() { l.Lock() go f() l.Lock() print(a) } 能保证打印出"hello,world"。第一次调用l.Unlock()(在f()中)先行发生于main中的第二次l.Lock()返回,先行发生于print。 Oncesync包的 var a string var once sync.Once func setup() { a = "hello,world" } func doprint() { once.Do(setup) print(a) } func twoprint() { go doprint() go doprint() } 调用 错误的同步方法注意,读操作r可能会看到并发的写操作w。即使这样也不能表明r之后的读能看到w之前的写。 var a,b int func f() { a = 1 b = 2 } func g() { print(b) print(a) } func main() { go f() g() }
var a string var done bool func setup() { a = "hello,world" done = true } func doprint() { if !done { once.Do(setup) } print(a) } func twoprint() { go doprint() go doprint() } 在 var a string var done bool func setup() { a = "hello,world" done = true } func main() { go setup() for !done { } print(a) } 和之前程序类似,在main中看到 type T struct { msg string } var g *T func setup() { t := new(T) t.msg = "hello,world" g = t } func main() { go setup() for g == nil { } print(g.msg) } 即使main看到了 (编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |