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:
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!
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 anil
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With