Why does this two-worker program take ~6s instead of ~3s?
Predict the number printed, and explain the timing.
func worker() chan int {
ch := make(chan int)
go func() {
time.Sleep(3 * time.Second)
ch <- 1
}()
return ch
}
func main() {
start := time.Now()
_, _ = <-worker(), <-worker()
println(int(time.Since(start).Seconds()))
}
Explain why it is not ~3s, and how to make it ~3s.
It prints 6. Go evaluates the two-operand expression left to right: the first <-worker() calls worker() (starting goroutine #1) and then blocks 3s receiving from it. Only after that completes does the second worker() even start, then blocks another 3s — so they run sequentially, ~6s total. To overlap them, start both first: c1, c2 := worker(), worker(); <-c1; <-c2 → ~3s.
- ✗Assuming Go starts both
worker()calls before doing either receive - ✗Thinking discarding with
_skips the blocking receive - ✗Believing only buffering, not reordering the starts, can overlap the work
- →What is Go's evaluation order for the operands of a multi-value expression?
- →Would buffering the channel change the ~6s result, and why or why not?
What it prints
6.
The expression <-worker(), <-worker() is evaluated left to right:
- The left operand
<-worker()is evaluated:worker()is called — goroutine #1 starts (it sleeps 3s), its channel is returned, andmainblocks receiving from it. After 3s the receive completes. - Only now is the right operand evaluated: the second
worker()is called — goroutine #2 starts only at this point, andmainblocks another 3s.
That is ~6 seconds total. Goroutine #2 does not even exist until the first receive has finished.
How to make it ~3s
Start both goroutines before the receives so their sleeps overlap:
c1, c2 := worker(), worker() // both goroutines start immediately
<-c1
<-c2 // both already slept in parallel → ~3s
Buffering the channel alone does not fix it: the cause is evaluation order, not a blocking send.