Назовите поведение select, в котором нет default и ни одна операция пока не готова.
select блокируется до тех пор, пока не станет доступна хотя бы одна операция из его ветвей. Если ни одна операция никогда не станет готовой, эта горутина останется заблокированной; если из-за этого во всей программе не останется выполняемой работы, рантайм завершит программу с ошибкой взаимной блокировки.
select решает задачу ожидания нескольких событий конкурентного взаимодействия без создания отдельной горутины для каждого канала. Такой подход особенно полезен в моделях, где канал представляет готовность результата, поступление задания или сигнал завершения.
Блокирующее поведение по умолчанию позволяет строить ожидание событий без активного опроса. Горутина не потребляет процессорное время, пока ни одна выбранная операция не может быть выполнена.
Рассмотрим обработчик, который должен ждать данные из нескольких источников. Если он проверяет готовность каналов в цикле с default, то может перейти к активному опросу и постоянно загружать процессор, не выполняя полезной работы.
Если же select не содержит default, но ни один источник не будет закрыт, не отправит значение и не станет доступен для отправки, ожидающая горутина не продолжит выполнение. Ошибка жизненного цикла каналов в таком случае может привести к утечке горутины или взаимной блокировке.
Когда выполняется select без default, Go проверяет готовность операций в его ветвях. Если готовой операции нет, текущая горутина приостанавливается, а планировщик может выполнять другие горутины.
Горутина возобновляется, когда хотя бы одна операция становится выполнимой. Для чтения это может быть отправка значения или закрытие канала; для отправки — появление получателя либо свободного места в буфере. После этого выбирается одна готовая ветвь, и выполняется только она.
Если готовых операций несколько, конкретная ветвь не должна рассматриваться как приоритетная. Код не должен зависеть от того, какая из одновременно готовых ветвей будет выбрана.
Наличие default меняет семантику: если в момент проверки нет готовой операции, выполняется default, и горутина не блокируется. Поэтому default применяют для намеренного неблокирующего шага, но не для обычного ожидания события.
Минимальный пример блокирующего ожидания:
Здесь чтение из left пока невозможно, а отправка в right делает вторую ветвь готовой. select ожидает готовность и затем принимает значение из right.
Важно отличать блокировку одной горутины от немедленной ошибки всей программы. Рантайм сообщает о взаимной блокировке только тогда, когда обнаруживает, что все горутины не могут продолжить выполнение; наличие работающей горутины, которая позднее разблокирует канал, предотвращает такой вывод.
Сервис получает задания из двух каналов: обычные задачи и приоритетные. Обработчик должен ждать поступления работы, а не постоянно проверять оба канала. Для этого выбирают select без default: ожидание не расходует CPU, а обработчик просыпается при появлении задания.
Вариант с циклом и default проще ошибочно превратить в busy loop: при отсутствии заданий он будет непрерывно выполнять проверки. Вариант с отдельной горутиной на каждый канал увеличивает число компонентов и требует дополнительной координации.
Блокирующий select — предпочтительное решение, если отсутствие события является нормальным состоянием. При этом система должна иметь определённый способ завершения обработчика: например, закрытие управляющего канала или появление отдельного сигнала остановки; иначе горутина может остаться ждать навсегда.
Вопрос: Освобождает ли заблокированный select процессорное ядро?
Ответ: Да, сама блокировка горутины не является активным ожиданием. Планировщик переводит её в состояние ожидания, поэтому другие готовые горутины могут выполняться. Однако большое число горутин, ожидающих события, всё равно увеличивает расход памяти и усложняет управление их жизненным циклом.
Вопрос: Что произойдёт с select, если все его каналы равны nil?
Ответ: Все операции на nil-каналах никогда не становятся готовыми, поэтому select без default блокируется навсегда. Это иногда используют намеренно для динамического отключения ветви, но случайное присваивание nil обычно означает ошибку конфигурации или жизненного цикла.
Вопрос: Можно ли считать отсутствие default гарантией, что какая-либо ветвь обязательно выполнится?
Ответ: Нет. Ветвь выполнится только если соответствующая операция когда-либо станет готовой. Если отправитель завершился без отправки, канал не будет закрыт, а альтернативного сигнала нет, ожидающая горутина останется заблокированной. Гарантию прогресса нужно обеспечивать протоколом завершения, например закрытием канала или отдельным сигналом отмены.