I am a newbie in golang and trying to experiment buffered channels with goroutines. I thought I understood how buffered channels work with goroutines until encountered the below example which becomes a brain teaser for me and gave a bang to the concepts that I have learned so far.
This is the original example that I took from the article https://medium.com/rungo/anatomy-of-channels-in-go-concurrency-in-go-1ec336086adb.
Code#1: (channel capacity=3, channel length=3, loop length=4)
func squares(c chan int) {
for i := 0; i <= 3; i++ {
num := <-c
fmt.Println(num * num)
}
}
func main() {
fmt.Println("main() started")
c := make(chan int, 3)
go squares(c)
c <- 1
c <- 2
c <- 3
fmt.Println("main() stopped")
}
Output:
main() started
main() stopped
Explanation: In the above program, channel c has a buffer capacity of 3. That means it can hold 3 values. Since the buffer is not overflowing (as we didn’t push any new value), the main goroutine will not block and the program exists. I have understood this example.
Code#2: (channel capacity=3, channel length=4, loop length=4)
func squares(c chan int) {
for i := 0; i <= 3; i++ {
num := <-c
fmt.Println(num * num)
}
}
func main() {
fmt.Println("main() started")
c := make(chan int, 3)
go squares(c)
c <- 1
c <- 2
c <- 3
c <- 4 // goroutine blocks here
fmt.Println("main() stopped")
}
Output:
main() started
1
4
9
16
main() stopped
Explanation: As now a filled buffer gets the push by c <- 4 send operation, main goroutine blocks and squares goroutine drains out all the values. It is also understood by me.
Code#3: (channel capacity=3, channel length=5, loop length=5)
func squares(c chan int) {
for i := 0; i <= 4; i++ {
num := <-c
fmt.Println(num * num)
}
}
func main() {
fmt.Println("main() started")
c := make(chan int, 3)
go squares(c)
c <- 1
c <- 2
c <- 3
c <- 4 // goroutine blocks here
c <- 5
fmt.Println("main() stopped")
}
Output:
main() started
1
4
9
16
25
main() stopped
Explanation: I have added another value to the channel which is 5. Although the channel capacity is only 3.
I understand that until the channel receives n+1 send operations, it won’t block the current goroutine. On the value 4, it receives n+1 operations, that's why goroutine gets blocked and drains out all the values but what I am unable to understand is that how n+2 operations are dealt by channel. Is it because we have read the values from the channel and we have more space for reading?
The channel capacity is not getting full here because your
squaresgoroutine is running and it immediately receives values that are being sent to the channel.At n+1 send operation, channel capacity is full so it will block. After at least one value is received from the channel (so a space is available to send next value) n+1 send operation continues and again capacity is full. Now at n+2 send operation, since the capacity is full so it will block until at least one value is received from channel and so on.