英文:
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 <-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()
}
}
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 <-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()
}
}
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论