我有多个goroutines试图同时在同一个频道上接收。似乎在频道上开始接收的最后一个goroutine获得了价值。这是语言规范中的某个地方还是未定义的行为?
c := make(chan string)
for i := 0; i < 5; i++ {
go func(i int) {
<-c
c <- fmt.Sprintf("goroutine %d", i)
}(i)
}
c <- "hi"
fmt.Println(<-c)
输出:
goroutine 4
修改
我刚才意识到它比我想象的要复杂得多。消息传递给所有goroutines。
c := make(chan string)
for i := 0; i < 5; i++ {
go func(i int) {
msg := <-c
c <- fmt.Sprintf("%s, hi from %d", msg, i)
}(i)
}
c <- "original"
fmt.Println(<-c)
输出:
original, hi from 0, hi from 1, hi from 2, hi from 3, hi from 4
答案 0 :(得分:49)
是的,这很复杂,但有一些经验法则可以让事情变得更加直截了当。
以下是您的计划的替代版本,应用这两个指南。这个案例展示了许多作家和作家。一个频道的读者:
c := make(chan string)
for i := 1; i <= 5; i++ {
go func(i int, co chan<- string) {
for j := 1; j <= 5; j++ {
co <- fmt.Sprintf("hi from %d.%d", i, j)
}
}(i, c)
}
for i := 1; i <= 25; i++ {
fmt.Println(<-c)
}
http://play.golang.org/p/quQn7xePLw
它创建了写入单个通道的五个例程,每个通道写入五次。主要的例行程序读取所有二十五条消息 - 您可能会注意到它们出现的顺序通常不是顺序的(即并发性很明显)。
此示例演示了Go通道的一项功能:可以让多个编写器共享一个通道; Go将自动交错消息。
同样适用于一个频道上的一个作家和多个读者,如第二个例子所示:
c := make(chan int)
var w sync.WaitGroup
w.Add(5)
for i := 1; i <= 5; i++ {
go func(i int, ci <-chan int) {
j := 1
for v := range ci {
time.Sleep(time.Millisecond)
fmt.Printf("%d.%d got %d\n", i, j, v)
j += 1
}
w.Done()
}(i, c)
}
for i := 1; i <= 25; i++ {
c <- i
}
close(c)
w.Wait()
此second example包括对主goroutine的等待,否则将立即退出并导致其他五个goroutine提前终止(感谢olov进行此更正)。
在两个示例中,都不需要缓冲。将缓冲视为仅性能增强器通常是一个很好的原则。如果您的程序没有没有缓冲区死锁,它也不会使用缓冲区死锁(但是相反的不是总是为真)。因此,作为另一条经验法则,请在没有缓冲的情况下启动,然后根据需要添加。
答案 1 :(得分:14)
迟到的回复,但我希望将来会像Long Polling, "Global" Button, Broadcast to everyone?
那样帮助其他人Effective Go解释了这个问题:
接收器始终会阻塞,直到有数据要接收为止。
这意味着你不能有超过1个goroutine监听1个频道,并期望所有goroutine都能收到相同的值。
运行此Code Example。
package main
import "fmt"
func main() {
c := make(chan int)
for i := 1; i <= 5; i++ {
go func(i int) {
for v := range c {
fmt.Printf("count %d from goroutine #%d\n", v, i)
}
}(i)
}
for i := 1; i <= 25; i++ {
c<-i
}
close(c)
}
即使有5个goroutines正在收听该频道,您也不会多次看到“计数1”。这是因为当第一个goroutine阻塞通道时,所有其他goroutine必须排队等候。当通道被解锁时,计数已经被接收并从通道中移除,因此下一个goroutine将获得下一个计数值。
答案 2 :(得分:6)
这很复杂。
另外,看看GOMAXPROCS = NumCPU+1
会发生什么。例如,
package main
import (
"fmt"
"runtime"
)
func main() {
runtime.GOMAXPROCS(runtime.NumCPU() + 1)
fmt.Print(runtime.GOMAXPROCS(0))
c := make(chan string)
for i := 0; i < 5; i++ {
go func(i int) {
msg := <-c
c <- fmt.Sprintf("%s, hi from %d", msg, i)
}(i)
}
c <- ", original"
fmt.Println(<-c)
}
输出:
5, original, hi from 0, hi from 4
并且,看看缓冲频道会发生什么。例如,
package main
import "fmt"
func main() {
c := make(chan string, 5+1)
for i := 0; i < 5; i++ {
go func(i int) {
msg := <-c
c <- fmt.Sprintf("%s, hi from %d", msg, i)
}(i)
}
c <- "original"
fmt.Println(<-c)
}
输出:
original
你也应该能够解释这些案例。
答案 3 :(得分:4)
我研究过现有的解决方案并创建了简单的广播库https://github.com/grafov/bcast。
group := bcast.NewGroup() // you created the broadcast group
go bcast.Broadcasting(0) // the group accepts messages and broadcast it to all members
member := group.Join() // then you join member(s) from other goroutine(s)
member.Send("test message") // or send messages of any type to the group
member1 := group.Join() // then you join member(s) from other goroutine(s)
val := member1.Recv() // and for example listen for messages
答案 4 :(得分:0)
对于多个goroutine在一个频道上收听,是的,这是可能的。关键点是消息本身,你可以定义一些这样的消息:
package main
import (
"fmt"
"sync"
)
type obj struct {
msg string
receiver int
}
func main() {
ch := make(chan *obj) // both block or non-block are ok
var wg sync.WaitGroup
receiver := 25 // specify receiver count
sender := func() {
o := &obj {
msg: "hello everyone!",
receiver: receiver,
}
ch <- o
}
recv := func(idx int) {
defer wg.Done()
o := <-ch
fmt.Printf("%d received at %d\n", idx, o.receiver)
o.receiver--
if o.receiver > 0 {
ch <- o // forward to others
} else {
fmt.Printf("last receiver: %d\n", idx)
}
}
go sender()
for i:=0; i<reciever; i++ {
wg.Add(1)
go recv(i)
}
wg.Wait()
}
输出是随机的:
5 received at 25
24 received at 24
6 received at 23
7 received at 22
8 received at 21
9 received at 20
10 received at 19
11 received at 18
12 received at 17
13 received at 16
14 received at 15
15 received at 14
16 received at 13
17 received at 12
18 received at 11
19 received at 10
20 received at 9
21 received at 8
22 received at 7
23 received at 6
2 received at 5
0 received at 4
1 received at 3
3 received at 2
4 received at 1
last receiver 4