当您使用范围通道中断 for 语句时会发生什么

我正在按照此代码获取带有频道的惰性数字范围


// iterator

func iterator(n int, c chan int) {

    for i := 0; i < n; i++ {

        c <- i

    }

    close(c)

    fmt.Println("iterator End")

}


c := make(chan int)

go iterator(5, c)

for i := range c {

    fmt.Println(i)

}

这将按预期打印


0

1

2

3

4

fmt.Println("iterator End")

但是当我像这样打破for循环时发生了什么


c := make(chan int)

go getNumbers(5, c)

for i := range c {

    if i == 2 {

        break

    }

    fmt.Println(i)

}

似乎 goroutine 被阻塞了,因为从不打印iterator End(我也尝试通过休眠主线程)。我想知道如何处理这种情况?我需要用它select来解决这个问题吗?有什么安全的方法可以检查范围是否中断并停止迭代器中的for 循环?


www说
浏览 143回答 1
1回答

慕的地6264312

如果一个 goroutine 写入一个无缓冲的通道并且没有其他 goroutine 从该通道读取 - 那么写入将永远阻塞。这将导致 goroutine 泄漏。这就是你正在经历的。如果你有一个写入通道的“生产者”goroutine,你需要一种方法来通知它停止。关闭通道不是这里的关键部分 - 因为通道超出范围时会被垃圾收集。阻塞的 goroutine(永远不会解除阻塞)被认为是泄漏,因为它永远不会被回收,所以你真的需要 goroutine 结束。您可以通过多种方式表示退出意图 - 最受欢迎的两种方式是:一个done频道;或者context.Context取消信号:完成通道func iterator(n int, c chan int, done <-chan struct{}) {&nbsp; &nbsp; for i := 0; i < n; i++ {&nbsp; &nbsp; &nbsp; &nbsp; select {&nbsp; &nbsp; &nbsp; &nbsp; case c <- i:&nbsp; &nbsp; &nbsp; &nbsp; case <-done:&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; break&nbsp; &nbsp; &nbsp; &nbsp; }&nbsp; &nbsp; }&nbsp; &nbsp; close(c)&nbsp; &nbsp; fmt.Println("iterator End")}阅读器协程:c := make(chan int)done := make(chan struct{})go iterator(5, c, done)for i := range c {&nbsp; &nbsp; if i == 2 {&nbsp; &nbsp; &nbsp; &nbsp; break&nbsp; &nbsp; }&nbsp; &nbsp; fmt.Println(i)}close(done) // signal writer goroutine to quit信号:context.Contextfunc iterator(ctx context.Context, n int, c chan int) {&nbsp; &nbsp; &nbsp; &nbsp; defer close(c)&nbsp; &nbsp; &nbsp; &nbsp; defer fmt.Println("iterator End")&nbsp; &nbsp; &nbsp; &nbsp; for i := 0; i < n; i++ {&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; select {&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; case c <- i:&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; case <-ctx.Done():&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; fmt.Println("canceled. Reason:", ctx.Err())&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; return&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; }&nbsp; &nbsp; &nbsp; &nbsp; }}阅读 goroutine:func run(ctx context.Context) {&nbsp; &nbsp; &nbsp; &nbsp; ctx, cancel := context.WithCancel(ctx)&nbsp; &nbsp; &nbsp; &nbsp; defer cancel()&nbsp; // call this regardless - avoid context leaks - but signals producer your intent to stop&nbsp; &nbsp; &nbsp; &nbsp; c := make(chan int)&nbsp; &nbsp; &nbsp; &nbsp; go iterator(ctx, 5, c)&nbsp; &nbsp; &nbsp; &nbsp; for i := range c {&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; if i == 2 {&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; break&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; }&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; fmt.Println(i)&nbsp; &nbsp; &nbsp; &nbsp; }}https://play.golang.org/p/4-fDyCurB7t
打开App,查看更多内容
随时随地看视频慕课网APP

相关分类

Go