这个频道是如何泄漏的?

huangapple go评论118阅读模式
英文:

How is this chan leaked?

问题

我正在尝试理解这张幻灯片上概述的问题:

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 在 chan 写入时被阻塞
  • goroutine 持有 chan 的引用
  • chan 永远不会被垃圾回收

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

英文:

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(&quot;tcp&quot;, 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 &lt;- sendMsg(msg, addr)
            fmt.Println(&quot;done&quot;)
        }(addr)
    }

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

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

    time.Sleep(time.Second)

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

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!

答案1

得分: 5

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

英文:

> The Go Programming Language Specification
>
> Function literals
>
> A function literal represents an anonymous function.
>
> FunctionLit = "func" Function .
>
> func(a, b int, z float64) bool { return a*b < int(z) }
>
> A function literal can be assigned to a variable or invoked directly.
>
> f := func(x, y int) int { return x + y }
> func(ch chan int) { ch <- ACK }(replyChan)
>
> Function literals are closures: they may refer to variables defined in
> a surrounding function. Those variables are then shared between the
> surrounding function and the function literal, and they survive as
> long as they are accessible.
>
>
> Send statements
>
> A send statement sends a value on a channel. The channel expression
> must be of channel type, the channel direction must permit send
> operations, and the type of the value to be sent must be assignable to
> the channel's element type.
>
> SendStmt = Channel "<-" Expression .
> Channel = Expression .
>
> Both the channel and the value expression are evaluated before
> communication begins. Communication blocks until the send can proceed.
> A send on an unbuffered channel can proceed if a receiver is ready. A
> send on a buffered channel can proceed if there is room in the buffer.
> A send on a closed channel proceeds by causing a run-time panic. A
> send on a nil channel blocks forever.

There is only one go statement, go func(addr string), and it's a closure over the channel variable errc.

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

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

Two goroutines are started since len(addrs) == 2. Because of a premature exit when err != nil on the first receive on channel errc, only one goroutine completes. The second goroutine is blocked on the send (write) to the unbuffered channel errc; it never completes. Therefore, there is still a reference to errc, so it's never garbage collected. The second goroutine is eventually abandoned when the program exits.

huangapple
  • 本文由 发表于 2013年8月1日 09:14:43
  • 转载请务必保留本文链接:https://go.coder-hub.com/17983699.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定