这个陈怎么泄露了?

时间:2013-08-01 01:14:43

标签: concurrency go garbage goroutine

我试图了解此幻灯片中列出的问题:

http://talks.golang.org/2013/bestpractices.slide#27

如果网址死亡,请复制代码:

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

评论:

  • goroutine在陈写上被阻止
  • goroutine持有对chan的引用
  • 陈永远不会被垃圾收集

我不确定我是否理解为什么chan永远不会被收集或者哪个goroutine保留了对chan的引用。感谢您的时间!

1 个答案:

答案 0 :(得分:5)

  

The Go Programming Language Specification

     

Function literals

     

函数文字表示匿名函数。

FunctionLit = "func" Function .

func(a, b int, z float64) bool { return a*b < int(z) }
     

函数文字可以分配给变量或直接调用。

f := func(x, y int) int { return x + y }
func(ch chan int) { ch <- ACK }(replyChan)
     

函数文字是闭包:它们可以引用在中定义的变量   周围的功能。然后在这些变量之间共享这些变量   周围函数和函数文字,它们存活下来   只要他们可以访问。

     

Send statements

     

send语句在通道上发送值。频道表达   必须是通道类型,通道方向必须允许发送   操作,以及要发送的值的类型必须可分配给   通道的元素类型。

SendStmt = Channel "<-" Expression .
Channel  = Expression .
     

之前评估通道和值表达式   沟通开始了。通信阻塞,直到发送可以继续。   如果接收器准备就绪,则可以继续在无缓冲信道上发送。一个   如果缓冲区中有空间,则可以继续发送缓冲通道。   封闭频道上的发送通过导致运行时panic来继续。一个   永远发送nil个频道块。

只有一个go语句go func(addr string),它是关于通道变量errc的闭包。

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
}

len(addrs) == 2以来开始了两个goroutines。由于err != nil在第一次接收频道errc时过早退出,因此只有一个goroutine完成。第二个goroutine在发送(写入)到无缓冲的通道errc时被阻止;它永远不会完成。因此,仍然存在对errc的引用,因此它永远不会被垃圾收集。当程序退出时,第二个goroutine最终被放弃了。