垃圾收集器会收集永远不会继续的Go协程吗?

huangapple go评论78阅读模式
英文:

Will the garbage collector collect Go routines that will never continue?

问题

考虑以下代码作为一个简化的例子:

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创建了一个名为printer的go例程,用于打印provide生成的数据。

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

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

英文:

Consider the following code as a simplified example:

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
    }
}

The function provide creates a go routine printer that prints the data provide generates.

My question is, what happens after provide returns and printer starts blocking on the empty channel. Will the go routine leak, as there is no further reference to c or will the garbage collector catch this case and dispose both the go routine and c?

If it is indeed the case that this kind of code causes a memory leak, what strategies can I do to prevent such a memory leak from happening?

答案1

得分: 10

关闭通道。从关闭的通道读取总是成功的,返回相应的零值。可选的第二个布尔返回值指示第一个值的有效性。

接收操作符
> 在赋值或初始化的形式中使用的接收表达式

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 { // 通道关闭
                        return
                }

                // v是有效的
                fmt.Println(v)
        }
}

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

        go printer(c)

        for i := 1; i <= 100; i++ {
                c <- i
        }
        close(c)
}
英文:

Close the channel. Reading from a closed channel always succeeds, returning a respective zero value. The optional second boolean returned value indicates validity of the first value.

Receive operator:
> A receive expression used in an assignment or initialization of the form

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

> yields an additional result of type bool reporting whether the communication succeeded. The value of ok is true if the value received was delivered by a successful send operation to the channel, or false if it is a zero value generated because the channel is closed and empty.

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)
}

答案2

得分: 0

尝试以下程序以验证确实存在内存泄漏。请注意,该程序会迅速消耗您的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) // 修复处

    go worker(c)

    for i := 0; i < 0xff; i++ {
        c <- i
    }
}

func main() {
    for {
        wrapper()
    }
}
英文:

Try the following program to verify that this indeed leaks memory. Please notice that this program eats up your RAM rather quickly; be prepared to kill it.

package main

func worker(c &lt;-chan int) {
	var i int

	for {
		i += &lt;-c
	}
}

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

	go worker(c)

	for i := 0; i &lt; 0xff; i++ {
		c &lt;- i
	}
}

func main() {
	for {
		wrapper()
	}
}

In order to resolve the leak, close a channel that is referenced by the now orphaned go routine. The runtime notices that a Go routine reading from only closed channels will never continue and proceeds to free it. The fixed code looks like this:

package main

func worker(c &lt;-chan int) {
	var i int

	for {
		i += &lt;-c
	}
}

func wrapper() {
	c := make(chan int)
    defer close(c) // fix here

	go worker(c)

	for i := 0; i &lt; 0xff; i++ {
		c &lt;- i
	}
}

func main() {
	for {
		wrapper()
	}
}

huangapple
  • 本文由 发表于 2013年5月16日 23:06:52
  • 转载请务必保留本文链接:https://go.coder-hub.com/16591117.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定