考虑这个程序:
package main
import "fmt"
import "time"
import "runtime"
func main() {
x := 0
go func() {
time.Sleep(500 * time.Millisecond)
x = 1
}()
for x == 0 {
runtime.Gosched()
}
fmt.Println("it works!")
}
为什么它会在本地终止但不在Playground终止?我的程序终止是否依赖于未定义的行为?
答案 0 :(得分:5)
Go游乐场使用time.Sleep
的特殊实现,旨在防止个别程序垄断网站的后端资源。
如this article about the how the playground is implemented中所述,调用time.Sleep()的goroutines将进入休眠状态。操场后端等待,直到所有其他goroutine被阻挡(否则会出现死锁),然后以最短的时间唤醒goroutine。
在你的程序中,你有两个goroutine:主要的一个,一个调用time.Sleep
。由于主goroutine永远不会阻塞,time.Sleep
调用永远不会返回。程序继续运行,直到它超过分配给它的CPU时间,然后终止。
答案 1 :(得分:4)
Go Memory Model不保证主程序可以观察到goroutine中写入x的值。在go routine destruction部分中给出了一个类似的错误程序作为示例。 Go Memory Model还专门用this section作为不正确的惯用语来调用繁忙的等待而没有同步。
你需要在goroutine中进行某种同步,以保证x = 1 发生在主循环中for循环的一次迭代之前。
这是一个保证按预期工作的程序版本。
http://play.golang.org/p/s3t5_-Q73W
package main
import (
"fmt"
"time"
)
func main() {
c := make(chan bool)
x := 0
go func() {
time.Sleep(500 * time.Millisecond)
x = 1
close(c) // 1
}()
for x == 0 {
<-c // 2
}
fmt.Println("it works!")
}
Go Memory Model保证标记为// 1 的行发生在标记为// 2的行之前。因此,for循环保证在第二次迭代之前终止。 / p>
答案 2 :(得分:2)
该代码不能提供太多保证。它几乎完全依赖于未定义行为的实现细节。
在大多数多线程系统中,无法保证在另一个没有障碍的线程中发生变化。你有一个可以在另一个处理器上运行的goroutine,它将一个值写入一个无人能保证读取的变量。
for x == 0 {
很容易被重写为for {
,因为从来没有保证可以看到对该变量的任何更改。
竞赛检测器也可能会报告此问题。你真的不应该期待这个工作。如果你想要一个sync.WaitGroup
,你应该只使用一个,因为它可以正确地跨线程协调。