下面的代码(http://play.golang.org/p/ikUtdoKOo5)应该向许多客户端广播消息。但它不起作用,我无法弄清楚原因。
package main
import "fmt"
type Broadcaster struct {
Clients []Client
}
func (b *Broadcaster) Broadcast(msg string) {
for _, c := range b.Clients {
go func() {
c.Inbox() <- msg
}()
}
}
type Client interface {
Inbox() chan string
}
type TestClient struct {
Messages chan string
}
func (tc TestClient) Inbox() chan string {
return tc.Messages
}
func main() {
client1 := TestClient{Messages: make(chan string)}
client2 := TestClient{Messages: make(chan string)}
broadcaster := Broadcaster{Clients: []Client{client1, client2}}
broadcaster.Broadcast("sos")
fmt.Printf("client1: '%s'\n", <-client1.Messages)
fmt.Printf("client2: '%s'\n", <-client2.Messages)
}
错误:
go run main.go
fatal error: all goroutines are asleep - deadlock!
goroutine 1 [chan receive]:
main.main()
/Users/artem/projects/gocode/src/github.com/artemave/broadcaster/main.go:36 +0x1f3
goroutine 3 [chan send]:
main.func·001()
/Users/artem/projects/gocode/src/github.com/artemave/broadcaster/main.go:12 +0x5f
created by main.(*Broadcaster).Broadcast
/Users/artem/projects/gocode/src/github.com/artemave/broadcaster/main.go:13 +0xcd
goroutine 4 [chan send]:
main.func·001()
/Users/artem/projects/gocode/src/github.com/artemave/broadcaster/main.go:12 +0x5f
created by main.(*Broadcaster).Broadcast
/Users/artem/projects/gocode/src/github.com/artemave/broadcaster/main.go:13 +0xcd
的更新:
go vet工具揭示了问题:
% go vet
main.go:12: range variable c enclosed by function
答案 0 :(得分:4)
您的问题是在闭包中使用变量c
。
由于c
是单个变量,因此在执行go例程时,c
很可能会设置为client2
,并且两条消息都将被广播到该客户端,并且没有到client1
。
使用函数参数修复它的一种方法:
func (b *Broadcaster) Broadcast(msg string) {
for _, c := range b.Clients {
go func(c Client) {
c.Inbox() <- msg
}(c)
}
}
答案 1 :(得分:4)
这是在for-range循环中重新分配c所带来的一个微妙的错误。它看起来有点奇怪,但你可以在std库中看到这个模式:
func (b *Broadcaster) Broadcast(msg string) {
for _, c := range b.Clients {
c := c // redeclare c for the closure
go func() {
c.Inbox() <- msg
}()
}
}