go - 为什么 goroutine 会泄漏

标签 go goroutine

我读了Twelve Go Best Practices并在第 30 页遇到有趣的例子。

func sendMsg(msg, addr string) error {
    conn, err := net.Dial("tcp", addr)
    if err != nil {
        return err
    }
    defer conn.Close()
    _, err = fmt.Fprint(conn, msg)
    return err
} 

func broadcastMsg(msg string, addrs []string) error {
    errc := make(chan error)
    for _, addr := range addrs {
        go func(addr string) {
            errc <- sendMsg(msg, addr)
            fmt.Println("done")
        }(addr)
    }

    for _ = range addrs {
        if err := <-errc; err != nil {
            return err
        }
    }
    return nil
}

func main() {
    addr := []string{"localhost:8080", "http://google.com"}
    err := broadcastMsg("hi", addr)

    time.Sleep(time.Second)

    if err != nil {
        fmt.Println(err)
        return
    }
    fmt.Println("everything went fine")
}

程序员提到,上面的代码发生了这种情况:

the goroutine is blocked on the chan write
the goroutine holds a reference to the chan
the chan will never be garbage collected

为什么goroutine会卡在这里?主线程被阻塞,直到它从 goroutine 接收到数据。在它继续 for 循环之后。不是吗?

为什么 errc chan 永远不会被垃圾回收?因为我没有关闭 channel ,goroutine 结束后?

最佳答案

我看到的一个问题是在 goroutines 启动后 broadcastMsg() 中:

for _ = range addrs {
    if err := <-errc; err != nil {
        return err
    }
}

如果从 errc 接收到非 nil errorbroadcastMsg() 会立即返回该错误并且不会从 channel 接收更多的值,这意味着更多的 goroutines 永远不会被解锁,因为 errc 是无缓冲的。

可能的修复

一个可能的解决方法是使用一个缓冲 channel ,它足够大,不会阻塞任何 goroutines,在这种情况下:

errc := make(chan error, len(addrs))

或者即使从 channel 接收到非nil error,仍然继续接收与在其上发送的 goroutines 一样多的次数:

var errRec error
for _ = range addrs {
    if err := <-errc; err != nil {
        if errRec == nil {
            errRec = err
        }
    }
}
return errRec

或如 slide #33 上的链接谈话中所述: 使用“退出” channel 来防止启动的 goroutines 在 broadcastMsg() 完成/返回后保持阻塞状态。

关于go - 为什么 goroutine 会泄漏,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/29892950/

相关文章:

go - fmt.Print in go routine *may* not output when master thread is loop

go - goroutine 没有输出

go - 为什么 App Engine Flexible Enviroment 不允许 WebSockets 和 HTTP/2 流量?

go - 在 Ubuntu 18.04 的 $ANDROID_HOME/ndk-bundle 和 $ANDROID_NDK_HOME 中都没有找到 Android NDK

go - 在 Go 中重复函数调用的 For 循环

mysql - 如何在golang服务中最小化下游服务中SQL注入(inject)的风险?

go - 从处理程序函数返回响应

multithreading - 当 Goroutines 切换时,CPU 上下文会发生什么?

go - 在单独的进程(多处理)上运行Goroutines

go - 在需要时中断 anaconda 的推特流