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

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

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 &quot;fmt&quot;

type Broadcaster struct {
	Clients []Client
}

func (b *Broadcaster) Broadcast(msg string) {
	for _, c := range b.Clients {
		go func() {
			c.Inbox() &lt;- 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(&quot;sos&quot;)

	fmt.Printf(&quot;client1: &#39;%s&#39;\n&quot;, &lt;-client1.Messages)
	fmt.Printf(&quot;client2: &#39;%s&#39;\n&quot;, &lt;-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&#183;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&#183;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() &lt;- 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() &lt;- msg
		}()
	}
}

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:

确定