广播员:所有的goroutine都处于休眠状态-死锁

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

Broadcaster: all goroutines are asleep - deadlock

问题

下面的代码(http://play.golang.org/p/ikUtdoKOo5)应该向多个客户端广播消息。但是它不起作用,我无法弄清楚原因。

  1. package main
  2. import "fmt"
  3. type Broadcaster struct {
  4. Clients []Client
  5. }
  6. func (b *Broadcaster) Broadcast(msg string) {
  7. for _, c := range b.Clients {
  8. go func() {
  9. c.Inbox() <- msg
  10. }()
  11. }
  12. }
  13. type Client interface {
  14. Inbox() chan string
  15. }
  16. type TestClient struct {
  17. Messages chan string
  18. }
  19. func (tc TestClient) Inbox() chan string {
  20. return tc.Messages
  21. }
  22. func main() {
  23. client1 := TestClient{Messages: make(chan string)}
  24. client2 := TestClient{Messages: make(chan string)}
  25. broadcaster := Broadcaster{Clients: []Client{client1, client2}}
  26. broadcaster.Broadcast("sos")
  27. fmt.Printf("client1: '%s'\n", <-client1.Messages)
  28. fmt.Printf("client2: '%s'\n", <-client2.Messages)
  29. }

错误信息:

  1. go run main.go
  2. fatal error: all goroutines are asleep - deadlock!
  3. goroutine 1 [chan receive]:
  4. main.main()
  5. /Users/artem/projects/gocode/src/github.com/artemave/broadcaster/main.go:36 +0x1f3
  6. goroutine 3 [chan send]:
  7. main.func·001()
  8. /Users/artem/projects/gocode/src/github.com/artemave/broadcaster/main.go:12 +0x5f
  9. created by main.(*Broadcaster).Broadcast
  10. /Users/artem/projects/gocode/src/github.com/artemave/broadcaster/main.go:13 +0xcd
  11. goroutine 4 [chan send]:
  12. main.func·001()
  13. /Users/artem/projects/gocode/src/github.com/artemave/broadcaster/main.go:12 +0x5f
  14. created by main.(*Broadcaster).Broadcast
  15. /Users/artem/projects/gocode/src/github.com/artemave/broadcaster/main.go:13 +0xcd

更新:

go vet 工具揭示了问题:

  1. % go vet
  2. 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 -->

  1. package main
  2. import &quot;fmt&quot;
  3. type Broadcaster struct {
  4. Clients []Client
  5. }
  6. func (b *Broadcaster) Broadcast(msg string) {
  7. for _, c := range b.Clients {
  8. go func() {
  9. c.Inbox() &lt;- msg
  10. }()
  11. }
  12. }
  13. type Client interface {
  14. Inbox() chan string
  15. }
  16. type TestClient struct {
  17. Messages chan string
  18. }
  19. func (tc TestClient) Inbox() chan string {
  20. return tc.Messages
  21. }
  22. func main() {
  23. client1 := TestClient{Messages: make(chan string)}
  24. client2 := TestClient{Messages: make(chan string)}
  25. broadcaster := Broadcaster{Clients: []Client{client1, client2}}
  26. broadcaster.Broadcast(&quot;sos&quot;)
  27. fmt.Printf(&quot;client1: &#39;%s&#39;\n&quot;, &lt;-client1.Messages)
  28. fmt.Printf(&quot;client2: &#39;%s&#39;\n&quot;, &lt;-client2.Messages)
  29. }

<br>
Error:

  1. go run main.go
  2. fatal error: all goroutines are asleep - deadlock!
  3. goroutine 1 [chan receive]:
  4. main.main()
  5. /Users/artem/projects/gocode/src/github.com/artemave/broadcaster/main.go:36 +0x1f3
  6. goroutine 3 [chan send]:
  7. main.func&#183;001()
  8. /Users/artem/projects/gocode/src/github.com/artemave/broadcaster/main.go:12 +0x5f
  9. created by main.(*Broadcaster).Broadcast
  10. /Users/artem/projects/gocode/src/github.com/artemave/broadcaster/main.go:13 +0xcd
  11. goroutine 4 [chan send]:
  12. main.func&#183;001()
  13. /Users/artem/projects/gocode/src/github.com/artemave/broadcaster/main.go:12 +0x5f
  14. created by main.(*Broadcaster).Broadcast
  15. /Users/artem/projects/gocode/src/github.com/artemave/broadcaster/main.go:13 +0xcd

<br>
UPDATE:

The go vet tool reveals the problem:

  1. % go vet
  2. main.go:12: range variable c enclosed by function

答案1

得分: 4

你的问题是在闭包中使用变量c的用法。
由于c是一个单一的变量,在执行go协程时,c很可能被设置为client2,并且两条消息都将被广播到该客户端,而不是client1

修复这个问题的一种方法是使用函数参数:

  1. func (b *Broadcaster) Broadcast(msg string) {
  2. for _, c := range b.Clients {
  3. go func(c Client) {
  4. c.Inbox() <- msg
  5. }(c)
  6. }
  7. }
英文:

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:

  1. func (b *Broadcaster) Broadcast(msg string) {
  2. for _, c := range b.Clients {
  3. go func(c Client) {
  4. c.Inbox() &lt;- msg
  5. }(c)
  6. }
  7. }

答案2

得分: 4

这是由于在for-range循环中重新分配c而引起的一个微妙的错误。看起来有点奇怪,但在标准库的几个地方可以看到这种模式:

  1. func (b *Broadcaster) Broadcast(msg string) {
  2. for _, c := range b.Clients {
  3. c := c // 为闭包重新声明c
  4. go func() {
  5. c.Inbox() <- msg
  6. }()
  7. }
  8. }

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:

  1. func (b *Broadcaster) Broadcast(msg string) {
  2. for _, c := range b.Clients {
  3. c := c // redeclare c for the closure
  4. go func() {
  5. c.Inbox() &lt;- msg
  6. }()
  7. }
  8. }

http://golang.org/doc/faq#closures_and_goroutines

huangapple
  • 本文由 发表于 2014年5月29日 06:06:44
  • 转载请务必保留本文链接:https://go.coder-hub.com/23922682.html
匿名

发表评论

匿名网友

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

确定