这是Go文档中"The Go Memory Model"
的示例代码:
var a string
var done bool
func setup() {
a = "hello, world"
done = true
}
func main() {
go setup()
for !done {
}
print(a)
}
文档中的原语:
和以前一样,不能保证在主体上观察完成的写入意味着观察对a的写入,因此该程序也可以打印空字符串。更糟糕的是,由于两个线程之间没有同步事件,因此无法保证main会一直执行写入操作。不能保证main中的循环会结束。
为什么?
我想主要的goroutine只是在寄存器中缓存 done = false 变量,而没有任何同步操作来刷新寄存器缓存。
谢谢。
编辑2019-08-16
我知道了。
如果设置了runtime.GOMAXPROCS(1)
,则唯一的线程将始终运行for !done{}
。调度程序没有任何机会将线程上下文切换到setup()
subgoroutine。因此,编译器无法保证此示例完成。
当我们调用内置lib函数时,运行时lib将有机会调度goroutine。例如:
mutex.Lock()
// 1、enter go build-in lib
// 2、switch to other goroutine if sync condition is not satisfied
// 3、condition is satisfied
someAction()
mutex.Unlock()
答案 0 :(得分:3)
您有一场数据竞赛。结果是不确定的。
$ go run -race racer.go
==================
WARNING: DATA RACE
Write at 0x00000052b581 by goroutine 6:
main.setup()
/home/peter/gopath/src/racer.go:8 +0x70
Previous read at 0x00000052b581 by main goroutine:
main.main()
/home/peter/gopath/src/racer.go:13 +0x56
Goroutine 6 (running) created at:
main.main()
/home/peter/gopath/src/racer.go:12 +0x46
==================
==================
WARNING: DATA RACE
Read at 0x000000510220 by main goroutine:
main.main()
/home/peter/gopath/src/racer.go:15 +0x74
Previous write at 0x000000510220 by goroutine 6:
main.setup()
/home/peter/gopath/src/racer.go:7 +0x3e
Goroutine 6 (finished) created at:
main.main()
/home/peter/gopath/src/racer.go:12 +0x46
==================
hello, worldFound 2 data race(s)
exit status 66
$
racer.go
:
package main
var a string
var done bool
func setup() {
a = "hello, world"
done = true
}
func main() {
go setup()
for !done {
}
print(a)
}
done = true
和!done
不同步是一场数据竞赛。
a = "hello, world"
和print(a)
不同步是一场数据竞赛。
答案 1 :(得分:-3)
当您使用go关键字运行一个函数时,它将打开一个新的goroutine,编译器将查看此goroutine并说:
'嘿,在此goroutine中,将done设置为true和a被设置为hello world完全不相关,因此我可以对它们进行排序,但这对我来说是最有效的。'
除非其他goroutine之间没有特定的链接,否则编译器不会查看它们。通道或锁。
这就是为什么它可能从不打印a的原因,因为可以先将done设置为true,以便for循环退出并在其他goroutine设置a之前穿过打印行。