Why is the main goroutine not blocked after write in unbuffered channel?

54 Views Asked by At
package main

import "fmt"

func square(c chan int) {
    fmt.Println("[square] reading")
    num := <-c
    fmt.Println("[square] before writing")
    c <- num * num
    fmt.Println("[square] after writing")
}

func cube(c chan int) {
    fmt.Println("[cube] reading")
    num := <-c
    fmt.Println("[cube] before writing")
    c <- num * num * num
    fmt.Println("[cube] after writing")
}

func main() {
    fmt.Println("[main] main() started")

    squareChan := make(chan int)
    cubeChan := make(chan int)

    go square(squareChan)
    go cube(cubeChan)

    testNum := 3
    fmt.Println("[main] sent testNum to squareChan")

    squareChan <- testNum

    fmt.Println("[main] resuming")
    fmt.Println("[main] sent testNum to cubeChan")

    cubeChan <- testNum // Why main not blocked here? 
 
    fmt.Println("[main] resuming")
    fmt.Println("[main] reading from channels")

    squareVal, cubeVal := <-squareChan, <-cubeChan
    sum := squareVal + cubeVal

    fmt.Println("[main] sum of square and cube of", testNum, " is", sum)
    fmt.Println("[main] main() stopped")
}

Why is it printed like that ??? Output:

1.[main] main() started
2. [main] sent testNum to squareChan
3. [cube] reading
4. [square] reading
5. [square] before writing
6. [main] resuming
7. [main] sent testNum to cubeChan
8. [main] resuming
9. [main] reading from channels
10. [square] after writing
11. [cube] before writing
12. [cube] after writing
13. [main] sum of square and cube of 3  is 36
14. [main] main() stopped

2 questions:

  1. Why after call "squareChan <- testNum" scheduler first check cube goroutine instead of square goroutine?
  2. Why after call "cubeChan <- testNum" main goroutine not blocked?

Please can somebody explain by steps - How exactly scheguler switch between square, cube and main goroutines in this code?

1

There are 1 best solutions below

0
coxley On
  1. Why after call "squareChan <- testNum" scheduler first check cube goroutine instead of square goroutine?

The scheduler isn't "checking" the cube routine because of the write to squareChan. Both goroutines are simply starting up, and have fmt.Println as the first logic.

If you only want statements that say when something has been read, you'd need to place that after receiving from the channel.

You can't rely on consistent starting order for goroutines. There are no guarantees there.

  1. Why after call "cubeChan <- testNum" main goroutine not blocked?

If squareChan <- testNum didn't block, why would this one?

The cube goroutine is waiting, blocked on receiving from the channel. When main writes to this channel, it's picked up immediately.

If you're genuinely curious about what events are being sequenced, collecting a trace profile may be of interest.