Go goroutines 泄漏

Go goroutines leaking

在 post 关于 go-routines 泄漏 https://www.ardanlabs.com/blog/2018/11/goroutine-leaks-the-forgotten-sender.html 之后,我试图解决我的泄漏代码。但是向通道添加缓冲区并没有起到作用。

我的代码

package main

import (
    "fmt"
    "runtime"
    "time"
)

func main() {
    fmt.Println(runtime.NumGoroutine())
    leaking()
    time.Sleep(5)
    fmt.Println(runtime.NumGoroutine())
}

func leaking() {
    errChang := make(chan int, 1)
    go func() {
        xx :=  return666()
        errChang <- xx
    }()
    fmt.Println("hola")
    return

    fmt.Println(<-errChang)
}

func return666() int {
    time.Sleep(time.Second * 1)
    return 6
}

我的初始代码没有使用缓冲区,导致 leaking 函数中的 go-routine,.. 泄漏。在 post 之后,我预计通过向通道添加缓冲区,可以避免泄漏。

Here, in the Go Playground,是您的原始代码,稍作修改:

  • 延迟减少,除了 time.Sleep(5) 变为 time.Sleep(time.Second);
  • a return 已删除,因为它变得不必要了;
  • a fmt.Println 被注释掉了,因为 return 和未注释的 fmt.Printlngo vet 抱怨无法到达 fmt.Println;
  • 存储在errChang中的频道更改为无缓冲。

当运行时,其输出为:

1
hola
2

(在 2 之前有一个小的延迟),表明你在函数 leaking 中启动的匿名 goroutine 仍然是 运行ning.

如果我们uncomment the commented-out fmt.Println,输出是:

1
hola
6
1

(在最终 1 之前有同样的轻微延迟)因为我们现在等待(然后打印)在 return666 中计算并通过通道 errChang 发送的值。

如果我们keep the commented-out fmt.Println commented out and make the channel buffered,输出变为:

1
hola
1

因为匿名 goroutine 现在可以将其值 (6) 推送到通道中。

通道本身以及存储在其中的单个值将被垃圾回收,因为此时没有对通道的剩余引用。但是请注意,简单地使通道缓冲并不总是足够的。如果我们send two values down the channel,程序returns打印:

1
hola
2

因为匿名 goroutine 成功地将 6 放入通道,但随后阻止尝试将 42 也放入。