Как оператор `select` в Go выбирает готовый к выполнению `case`?

Ответ

Оператор select ведет себя по-разному в зависимости от состояния каналов.

  1. Если готов только один case: Выполняется именно он.
  2. Если готовы несколько case: Выбор одного из них происходит псевдослучайно. Это сделано для обеспечения справедливости (fairness) и предотвращения "голодания" (starvation), когда один и тот же case выбирался бы всегда, блокируя остальные.

    ch1 := make(chan int, 1)
    ch2 := make(chan int, 1)
    ch1 <- 1
    ch2 <- 2
    
    select {
    case v := <-ch1:
        fmt.Println("Получено из ch1:", v)
    case v := <-ch2:
        fmt.Println("Получено из ch2:", v)
    }
    // При многократном запуске вывод будет разным
  3. Если ни один case не готов: select блокирует выполнение горутины до тех пор, пока один из case не станет готов.
  4. Если есть ветка default: Если ни один case не готов в момент проверки, выполняется ветка default. Это делает select неблокирующим.

    ch := make(chan int)
    
    select {
    case v := <-ch:
        fmt.Println("Получено:", v)
    default:
        fmt.Println("Никто не готов, выполняем default")
    }