Here is an example:
func main() {
c := make(chan int)
i := 0
go goroutine(c)
c <- i
time.Sleep(10 * time.Second)
}
func goroutine(c chan int) {
for {
num := <- c
fmt.Println(num)
num++
time.Sleep(1 * time.Second)
c <- num
}
}
What I'm trying to do inside of goroutine is to receive number from channel, print it, increment and after one second send it back to the channel. After this I want to repeat the action.
But as a result, operation is only done once.
Output:
0
Am I doing something wrong?
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)
go receive(ch)
}
You use unbuffered channel, so your goroutine hang on c <- num
You should use buffered channel, like this: c := make(chan int, 1)
Try it on the Go playground
You create an unbuffered channel c
with
c := make(chan int)
On unbuffered channels, operations are symmentrical, i.e. every send on a channel needs exactly one receive, every receive needs exactly one send. You send your i
into the channel, the goroutine receives it into num
. After that, the goroutine sends the incremented num
into the channel, but nobody is there to receive it.
In short: The statement
c <- num
will block.
You could use a 1-buffered channel, that should work.
There is another problem with your code that you solved by waiting ten seconds in main
: You don't know when your goroutine finishes. Typically, a sync.WaitGroup
is used in these situations. But: Your goroutine does not finish. It's idiomatic to introduce a chan struct{}
that you close in your main goroutine after a while and select
over both channels in the worker goroutine.