为了账号安全,请及时绑定邮箱和手机立即绑定

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

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

Go
www说 2022-06-21 16:09:12
我正在按照此代码获取带有频道的惰性数字范围// iteratorfunc 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)}这将按预期打印01234fmt.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 循环?
查看完整描述

1 回答

?
慕的地6264312

TA贡献1817条经验 获得超6个赞

如果一个 goroutine 写入一个无缓冲的通道并且没有其他 goroutine 从该通道读取 - 那么写入将永远阻塞。这将导致 goroutine 泄漏。这就是你正在经历的。


如果你有一个写入通道的“生产者”goroutine,你需要一种方法来通知它停止。关闭通道不是这里的关键部分 - 因为通道超出范围时会被垃圾收集。阻塞的 goroutine(永远不会解除阻塞)被认为是泄漏,因为它永远不会被回收,所以你真的需要 goroutine 结束。


您可以通过多种方式表示退出意图 - 最受欢迎的两种方式是:


一个done频道;或者

context.Context取消

信号:完成通道

func iterator(n int, c chan int, done <-chan struct{}) {

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

        select {

        case c <- i:

        case <-done:

            break

        }

    }

    close(c)

    fmt.Println("iterator End")

}

阅读器协程:


c := make(chan int)

done := make(chan struct{})

go iterator(5, c, done)

for i := range c {

    if i == 2 {

        break

    }

    fmt.Println(i)

}

close(done) // signal writer goroutine to quit

信号:context.Context

func iterator(ctx context.Context, n int, c chan int) {

        defer close(c)

        defer fmt.Println("iterator End")


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

                select {

                case c <- i:

                case <-ctx.Done():

                        fmt.Println("canceled. Reason:", ctx.Err())

                        return

                }

        }

}

阅读 goroutine:


func run(ctx context.Context) {

        ctx, cancel := context.WithCancel(ctx)

        defer cancel()  // call this regardless - avoid context leaks - but signals producer your intent to stop

        c := make(chan int)

        go iterator(ctx, 5, c)

        for i := range c {

                if i == 2 {

                        break

                }

                fmt.Println(i)

        }

}

https://play.golang.org/p/4-fDyCurB7t


查看完整回答
反对 回复 2022-06-21
  • 1 回答
  • 0 关注
  • 142 浏览
慕课专栏
更多

添加回答

举报

0/150
提交
取消
微信客服

购课补贴
联系客服咨询优惠详情

帮助反馈 APP下载

慕课网APP
您的移动学习伙伴

公众号

扫描二维码
关注慕课网微信公众号