5

I'm trying to understand the problem outlined on this slide:

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

Copying the code in case the URL dies:

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

And the comments:

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

I'm not sure I understand why the chan never gets collected or which goroutine is keeping a reference to the chan. Your time is appreciated!

4

1 回答 1

5

Go 编程语言规范

函数字面量

函数字面量表示匿名函数。

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)

函数字面量是闭包:它们可以引用在周围函数中定义的变量。然后,这些变量在周围的函数和函数字面量之间共享,只要它们可以访问,它们就会继续存在。

发送报表

发送语句在通道上发送一个值。通道表达式必须是通道类型,通道方向必须允许发送操作,并且要发送的值的类型必须可分配给通道的元素类型。

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

通道和值表达式都在通信开始之前进行评估。通信阻塞,直到发送可以继续。如果接收器准备好,则可以在无缓冲通道上进行发送。如果缓冲区中有空间,则可以继续缓冲通道上的发送。关闭通道上的发送通过导致运行时进行panicnil通道上的发送永远阻塞。

只有一个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. 由于err != nil在第一次接收通道时过早退出errc,只有一个 goroutine 完成。第二个 goroutine 在发送(写入)到无缓冲通道时被阻塞errc;它永远不会完成。因此,仍然存在对 的引用errc,因此它永远不会被垃圾收集。当程序退出时,第二个 goroutine 最终被放弃。

于 2013-08-01T05:12:22.787 回答