如何解决使用渠道永久退出循环-Go Playground问题
我正在尝试实现一种简单的逻辑,其中 Producer 通过永久的ch
循环将数据发送到通道for
,并且消费者从通道{{1 }}。
生产者在通道ch
上收到信号时,将停止生产并退出永久循环。
代码是这样的(另请参见this playground)
quit
如果我在计算机(带有4核的Mac)上运行这段代码,则一切正常。如果我在 Go Playgroud 上尝试相同的代码,它将始终超时。我猜这是因为Go Playground是一个单核,因此无限循环不会给其他goroutine运行机会,但是我不明白为什么指令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)
}
}
}
没有任何作用。>
仅是为了完成图片,我已经看到,如果在Mac上设置runtime.Gosched()
,该程序仍然可以正常工作并按预期退出。如果我在Mac上设置GOMAXPROCS=1
并删除了GOMAXPROCS=1
指令,该行为将变得很脆弱:有时程序会按预期终止,有时似乎永远不会退出无限循环。
解决方法
您创建了在实际程序中不应该发生的病理情况,因此调度程序并未针对此情况进行优化。结合在操场上伪造的时间实现,在达到超时之前,生产者和消费者的周期过多。
生产者goroutine正在尽可能快地创建值,而消费者始终准备好接收它们。使用GOMAPXPROCS=1
,调度程序会花费所有时间在这两者之间进行跳动,然后才被迫抢占可用的工作以检查主goroutine,这比游乐场所允许的时间更长。
如果我们为生产者-消费者对添加一些内容,我们可以限制他们独占调度程序的时间。例如,向使用者添加time.Sleep(time.Microsecond)
将导致游乐场打印1000个值。这也说明了模拟时间在操场上的“准确度”,因为对于普通的硬件而言这是不可能的,而普通的硬件需要花费非零时间来处理每条消息。
虽然很有趣,但这与实际程序无关。
一些注意事项,您可以range
通过某个通道来接收所有值,如果可能,应始终在goroutine的开始处始终defer wg.Done
,可以在select
中发送值case
允许您在发送尚未准备好时实际上取消for-select循环,如果您想要“退出使用者”消息,则还需要将WaitGroup
发送给使用者。
https://play.golang.org/p/WyPmpY9pFl7
func main() {
ch := make(chan int)
quit := make(chan bool)
var wg sync.WaitGroup
wg.Add(2)
go produce(ch,quit,&wg)
go consume(ch,&wg)
time.Sleep(50 * time.Microsecond)
fmt.Println("CLOSE")
close(quit)
wg.Wait()
}
func produce(ch chan int,quit chan bool,wg *sync.WaitGroup) {
defer wg.Done()
for i := 0; ; i++ {
select {
case <-quit:
close(ch)
fmt.Println("exit")
return
case ch <- i:
fmt.Println("Producer sends",i)
}
}
}
func consume(ch chan int,wg *sync.WaitGroup) {
defer wg.Done()
for i := range ch {
fmt.Println("Consumer receives",i)
time.Sleep(time.Microsecond)
}
fmt.Println("exit consumer")
return
}
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。