Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Multiple receivers on a single channel. Who gets the data?

Unbuffered channels block receivers until data is available on the channel. It's not clear to me how this blocking behaves with multiple receivers on the same channel (say when using goroutines). I am sure they would all block as long as there is no data sent on that channel.
But what happens once I send a single value to that channel? Which receiver/goroutine will get the data and therefore unblock? All of them? The first in line? Random?

like image 581
Leo Houer Avatar asked Jul 21 '15 13:07

Leo Houer


People also ask

Can 2 Goroutines read from the same channel?

A channel is a communication object using which goroutines can communicate with each other. Technically, a channel is a data transfer pipe where data can be passed into or read from. Hence one goroutine can send data into a channel, while other goroutines can read that data from the same channel.

What is a goroutine?

A goroutine is a function that executes simultaneously with other goroutines in a program and are lightweight threads managed by Go. A goroutine takes about 2kB of stack space to initialize.


2 Answers

A single random (non-deterministic) one will receive it.

See the language spec:

Execution of a "select" statement proceeds in several steps:

  1. For all the cases in the statement, the channel operands of receive operations and the channel and right-hand-side expressions of send statements are evaluated exactly once, in source order, upon entering the "select" statement. The result is a set of channels to receive from or send to, and the corresponding values to send. Any side effects in that evaluation will occur irrespective of which (if any) communication operation is selected to proceed. Expressions on the left-hand side of a RecvStmt with a short variable declaration or assignment are not yet evaluated.
  2. If one or more of the communications can proceed, a single one that can proceed is chosen via a uniform pseudo-random selection. Otherwise, if there is a default case, that case is chosen. If there is no default case, the "select" statement blocks until at least one of the communications can proceed.
  3. Unless the selected case is the default case, the respective communication operation is executed.
  4. If the selected case is a RecvStmt with a short variable declaration or an assignment, the left-hand side expressions are evaluated and the received value (or values) are assigned.
  5. The statement list of the selected case is executed.
like image 94
Stephan Dollberg Avatar answered Oct 23 '22 12:10

Stephan Dollberg


By default the goroutine communication is synchronous and unbuffered: sends do not complete until there is a receiver to accept the value. There must be a receiver ready to receive data from the channel and then the sender can hand it over directly to the receiver.

So channel send/receive operations block until the other side is ready:

1. A send operation on a channel blocks until a receiver is available for the same channel: if there’s no recipient for the value on ch, no other value can be put in the channel. And the other way around: no new value can be sent in ch when the channel is not empty! So the send operation will wait until ch becomes available again.

2. A receive operation for a channel blocks until a sender is available for the same channel: if there is no value in the channel, the receiver blocks.

This is illustrated in the below example:

package main
import "fmt"

func main() {
    ch1 := make(chan int)
    go pump(ch1) // pump hangs
    fmt.Println(<-ch1) // prints only 0
}

func pump(ch chan int) {
    for i:= 0; ; i++ {
        ch <- i
    }
}

Because there is no receiver the goroutine hangs and print only the first number.

To workaround this we need to define a new goroutine which reads from the channel in an infinite loop.

func receive(ch chan int) {
    for {
        fmt.Println(<- ch)
    }
}

Then in main():

func main() {
    ch := make(chan int)
    go pump(ch)
    receive(ch)
}
like image 37
Endre Simo Avatar answered Oct 23 '22 10:10

Endre Simo