【问题标题】:Why goroutine leaks为什么 goroutine 泄漏
【发布时间】:2015-07-05 17:47:33
【问题描述】:

我阅读了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 永远不会被垃圾回收?因为我没有关闭通道,goroutine完成后?

【问题讨论】:

    标签: go goroutine


    【解决方案1】:

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

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

    如果从errc 接收到非nil errorbroadcastMsg() 立即返回该错误并且不会从通道接收更多值,这意味着进一步的 goroutine 将永远不会被解除阻塞,因为errc没有缓冲。

    可能的修复

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

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

    或者即使从通道接收到非nil error,仍然继续接收与发送的 goroutine 一样多的次数:

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

    或者在slide #33 的链接谈话中提到:使用“退出”通道来防止启动的 goroutine 在broadcastMsg() 完成/返回后保持阻塞。

    【讨论】:

      【解决方案2】:

      您有一个包含两个地址(本地主机、谷歌)的列表。您正在向其中的每一个发送消息(嗨),每个地址使用一个 goroutine。并且 goroutine 将错误(可能是 nil)发送到 errc 通道。

      如果您向通道发送内容,则还需要从该通道读取值的内容,否则它将阻塞(除非它是缓冲通道,但即使缓冲通道在其缓冲区已满时也会阻塞)。

      所以你的阅读循环看起来像这样:

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

      如果第一个地址返回一个不为 nil 的错误,则循环返回。随后的错误值永远不会从通道中读取,因此它会阻塞。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2011-09-11
        • 2011-10-25
        相关资源
        最近更新 更多