Multiple mountains listening to one channel

I have several goroutines trying to get on the same channel at the same time. It seems that the last gorutin that starts to receive through the channel is getting the value. Is it somewhere in the language specification or is this behavior undefined?

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) 

Output:

 goroutine 4 

Playground example

EDIT:

I only realized that it was harder than I thought. A message is sent across all 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) 

Output:

 original, hi from 0, hi from 1, hi from 2, hi from 3, hi from 4 

Playground example

+43
go
Mar 30 '13 at 6:14
source share
5 answers

Yes, it’s complicated, but there are a few rules of thumb that should make things more clear.

  • prefer to use formal arguments for channels that you pass go-routines instead of accessing channels in a global scope. You can get more compiler by checking this method, as well as better modularity.
  • avoid reading and writing on the same channel in a certain routine (including the "main" one). Otherwise, deadlock is a much greater risk.

Here is an alternative version of your program that uses these two guides. This case shows a lot of writers and one reader on the channel:

 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

It creates five routines that record one channel, each of which is recorded five times. The main routine reads all twenty-five messages - you may notice that the order in which they appear is often not sequential (i.e. concurrency).

This example demonstrates the function of the Go channels: perhaps several authors have one channel; Go will alternate messages automatically.

The same applies to one author and several readers on the same channel, as shown in the second example:

 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() 

This second example includes the wait imposed on the main goroutine, which would otherwise exit quickly and cause the other five goroutines to be completed earlier (thanks to olov for this correction).

In both examples, buffering was not required. As a general rule, viewing buffering as a performance enhancer is a good principle. If your program does not get into a dead end without buffers, it will also not be blocked by buffers (but the opposite is not always true). So, as another rule of thumb, start without buffering, and then add it as needed .

+39
Mar 30 '13 at 17:30
source share

Late answer, but I hope this helps others in the future, such as the Long Polling, "Global" Button, broadcast for everyone?

An effective step explains the problem:

Receivers are always blocked until data is received.

This means that you cannot have more than 1 goroutine listening to 1 channel, and expect ALL GOROTINS to get the same value.

Run this sample code .

 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) } 

You will not see "count 1" more than once, although there are 5 goroutines that listen to the channel. This is due to the fact that when the first gorutin blocks the channel, all other gorutins must wait in line. When the channel is unlocked, the account is already accepted and removed from the channel, so the next goroutine in the line receives the next account value.

+10
Nov 06 '13 at 17:16
source share

It's complicated.

Also see what happens with GOMAXPROCS = NumCPU+1 . For example,

 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) } 

Output:

 5, original, hi from 0, hi from 4 

And look what happens with buffered channels. For example,

 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) } 

Output:

 original 

You can also explain these cases.

+5
Mar 30 '13 at 6:55
source share

I studied existing solutions and created a simple broadcast library 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 
+2
Dec 22 '13 at 21:31
source share

For several goroutines, listen on one channel, yes, it is possible. the key point is the message itself, you can define such a message as follows:

 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() } 

The output is random:

 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 
0
Jul 08 '17 at 2:07 on
source share



All Articles