英文:
Broadcaster: all goroutines are asleep - deadlock
问题
下面的代码(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
英文:
The code below (http://play.golang.org/p/ikUtdoKOo5) is supposed to broadcast a message to a number of clients. But it does not work and I can't figure out why.
<!-- golang -->
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)
}
<br>
Error:
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
<br>
UPDATE:
The go vet tool reveals the problem:
% go vet
main.go:12: range variable c enclosed by function
答案1
得分: 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)
}
}
英文:
Your problem is the usage of the variable c
in the closure.
Since c
is a single variable, at the time when the go routines are executed, c
will most likely be set to client2
, and both messages will be broadcasted to that client, and none to client1
.
One way to fix it using function parameter:
func (b *Broadcaster) Broadcast(msg string) {
for _, c := range b.Clients {
go func(c Client) {
c.Inbox() <- msg
}(c)
}
}
答案2
得分: 4
这是由于在for-range循环中重新分配c而引起的一个微妙的错误。看起来有点奇怪,但在标准库的几个地方可以看到这种模式:
func (b *Broadcaster) Broadcast(msg string) {
for _, c := range b.Clients {
c := c // 为闭包重新声明c
go func() {
c.Inbox() <- msg
}()
}
}
http://golang.org/doc/faq#closures_and_goroutines
英文:
This is a subtle bug brought on by the reassignment of c in the for-range loop. It looks a little strange, but you see this pattern in the std library in a few places:
func (b *Broadcaster) Broadcast(msg string) {
for _, c := range b.Clients {
c := c // redeclare c for the closure
go func() {
c.Inbox() <- msg
}()
}
}
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论