英文:
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("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!
答案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 <- sendMsg(msg, addr)
fmt.Println("done")
}(addr)
}
for _ = range addrs {
if err := <-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.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论