Do select statements guarantee order of channel selection?

Issue

Following on from this answer, if a goroutine is selecting on two channels, is it guaranteed that the channels are selected in the same order that they are sent on? I’m particularly interested in the case where the sender is single threaded.

As an example, is it guaranteed that this code always produces the same output:

package main

import (
  "fmt"
  "time"
)

var x, y chan int

func worker() {
  for {
    select {
    case v := <- x:
      fmt.Println(v)
    case v := <- y:
      fmt.Println(v)
    }
  }
}

func main() {
  x = make(chan int)
  y = make(chan int)
  go worker()
  x <- 1
  y <- 2
  <- time.After(1 * time.Second)
}

My experiments would seem to indicate that this is guaranteed for unbuffered channels, as shown, but not guaranteed for buffered channels. Can someone confirm that please?

Solution

Although in your example it’s guaranteed that you’ll see 1 printed first, but not because of how select works.

It’s because x is an unbuffered channel, and send on it will block until someone receives from it, and only then can you send on channel y.

So your select inside worker() won’t see 2 ready channels at the same time.

It if would, then one is chosen pseudo-randomly. For details, see How does select work when multiple channels are involved?

So note that if you’d use buffered channels:

x = make(chan int, 1)
y = make(chan int, 1)

Then send on x and y wouldn’t block, and now it’s up to the goroutine scheduler how goroutines are executed. It’s possible the main goroutine can send both values before the worker() goroutine reaches and evaluates the channel operations, and so you could see 1 then 2 printed, just as well as 2 and then 1.

Answered By – icza

Answer Checked By – Pedro (GoLangFix Volunteer)

Leave a Reply

Your email address will not be published.