【问题标题】:Will the garbage collector collect Go routines that will never continue?垃圾收集器会收集永远不会继续的 Go 例程吗?
【发布时间】:2013-05-11 13:31:45
【问题描述】:

将以下代码视为一个简化示例:

func printer(c <-chan int) {
    for {
        fmt.Print(<-c)
    }
}

func provide() {
    c := make(chan int)

    go printer(c)

    for i := 1; i <= 100; i++ {
        c <- i
    }
}

函数provide 创建一个goroutine printer 打印provide 生成的数据。

我的问题是,provide 返回并且printer 开始阻塞空通道后会发生什么。 go 例程是否会泄漏,因为没有进一步引用 c 还是垃圾收集器会捕获这种情况并处理 go 例程和 c

如果确实是这种代码导致了内存泄漏,我可以采取什么策略来防止这种内存泄漏的发生?

【问题讨论】:

    标签: memory-leaks go goroutine


    【解决方案1】:

    关闭频道。从关闭的通道读取始终成功,并返回相应的零值。可选的第二个布尔返回值表示第一个值的有效性。

    Receive operator:

    在表单的赋值或初始化中使用的接收表达式

    x, ok = <-ch
    x, ok := <-ch
    var x, ok = <-ch
    

    产生bool 类型的附加结果,报告通信是否成功。 ok 的值如果接收到的值是通过成功的发送操作传递到通道,则为 true,如果它是由于通道关闭且为空而生成的零值,则为 false。

    func printer(c <-chan int) {
            for {
                    v, ok := <-c
                    if !ok { // chan closed
                            return
                    }
    
                    // v is valid
                    fmt.Println(v)
            }
    }
    
    func provide() {
            c := make(chan int)
    
            go printer(c)
    
            for i := 1; i <= 100; i++ {
                    c <- i
            }
            close(c)
    }
    

    【讨论】:

    • 谢谢。显然我的“解决方案”并没有真正奏效。
    • 在打印机 goroutine 中使用 range 子句将避免 ok 检查 -> for v := range c { fmt.Println(v) }
    【解决方案2】:

    尝试以下程序来验证这确实会泄漏内存。请注意,该程序会很快耗尽您的 RAM;准备杀死它。

    package main
    
    func worker(c <-chan int) {
        var i int
    
        for {
            i += <-c
        }
    }
    
    func wrapper() {
        c := make(chan int)
    
        go worker(c)
    
        for i := 0; i < 0xff; i++ {
            c <- i
        }
    }
    
    func main() {
        for {
            wrapper()
        }
    }
    

    为了解决泄漏,请关闭现在孤立的 go 例程引用的通道。运行时会注意到仅从关闭的通道读取的 Go 例程将永远不会继续并继续释放它。固定代码如下所示:

    package main
    
    func worker(c <-chan int) {
        var i int
    
        for {
            i += <-c
        }
    }
    
    func wrapper() {
        c := make(chan int)
        defer close(c) // fix here
    
        go worker(c)
    
        for i := 0; i < 0xff; i++ {
            c <- i
        }
    }
    
    func main() {
        for {
            wrapper()
        }
    }
    

    【讨论】:

      猜你喜欢
      • 2015-04-10
      • 2012-12-19
      • 1970-01-01
      • 1970-01-01
      • 2017-01-19
      • 1970-01-01
      • 2015-05-04
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多