微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

使用渠道永久退出循环-Go Playground问题

如何解决使用渠道永久退出循环-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 举报,一经查实,本站将立刻删除。