Search code examples
goconcurrencygoroutinechannel

How does the behavior of a buffered channel differ from my expectations in Go?


I am trying to understand how a buffered channel works and wrote a code snippet for it

package main

import (
    "fmt"
)

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 // blocks here

    fmt.Println("main() stopped")
}

As per how I expected the program to behave, the main goroutine starts and continues till c<-4, which at that point gets blocked and the control goes to square goroutine (as the buffer capacity is 3). The loop in the squares goroutine continues till 4th iteration at which point the channel is empty. A read operation on an empty channel is blocking and so the control goes back to main goroutine. At that point the write operation to the channel (c<-4) gets executed, we print "main() stopped" and the program ends.

Meaning I expected the output to be,

main() started
1
4
9
main() stopped

But I get an output,

main() started
1
4
9
16
main() stopped

How? Am I missing something about how exactly channels work?


Solution

  • That is not how channels work.

    Goroutines run concurrently. That means when a goroutine sends to a buffered channel, another goroutine waiting to receive from that channel can receive it immediately. It does not wait for the channel to fill up.

    As for the ending of the program, when you send the last number to the channel, there is no guarantee that the goroutine will pick it up and print the output before the program ends, because you are not waiting for the goroutine to complete. So, by luck, it runs and prints the output. There will be other executions where this does not happen, and the program terminates before the goroutine can print the output.