我正在尝试实现一个简单的逻辑,其中Producer将数据发送到ch
具有永久for
循环的通道,而 Consumer 从通道读取ch
。
Producer 在通道上接收到信号时停止生产并退出永久循环quit
。
代码是这样的(另见这个游乐场)
func main() {
ch := make(chan int)
quit := make(chan bool)
var wg sync.WaitGroup
wg.Add(1)
go produce(ch, quit, &wg)
go consume(ch)
time.Sleep(1 * time.Millisecond)
fmt.Println("CLOSE")
close(quit)
wg.Wait()
}
func produce(ch chan int, quit chan bool, wg *sync.WaitGroup) {
for i := 0; ; i++ {
select {
case <-quit:
close(ch)
fmt.Println("exit")
wg.Done()
return //we exit
default:
ch <- i
fmt.Println("Producer sends", i)
}
}
}
func consume(ch chan int) {
for {
runtime.Gosched() // give the opportunity to the main goroutine to close the "quit" channel
select {
case i, more := <-ch:
if !more {
fmt.Println("exit consumer")
return
}
fmt.Println("Consumer receives", i)
}
}
}
如果我在我的机器(一台 4 核的 Mac)上运行这段代码,一切正常。如果我在Go Playgroud上尝试相同的代码,它总是会超时。我猜这是因为 Go Playground 是单核,因此无限循环不会给其他 goroutine 运行的机会,但是我不明白为什么该指令runtime.Gosched()
没有任何效果。
只是为了完成我所看到的图片,如果我GOMAXPROCS=1
在我的 Mac 上设置,该程序仍然可以正常工作并按预期退出。如果我GOMAXPROCS=1
在我的 Mac 上设置并删除runtime.Gosched()
指令,行为会变得脆弱:有时程序会按预期终止,有时它似乎永远不会退出无限循环。