Ответ
Когда горутина пытается прочитать данные из пустого канала, она блокируется.
Это ключевой механизм синхронизации в Go. Вот что происходит на более низком уровне:
- Переход в состояние ожидания: Горутина, выполняющая блокирующую операцию чтения (
<-myChannel
), переходит в состояниеwaiting
. - Освобождение потока ОС: Планировщик Go (Go scheduler) снимает эту горутину с выполнения и убирает ее из очереди готовых к выполнению (
runnable
) горутин. Поток операционной системы (M
), на котором она выполнялась, освобождается и может быть использован для выполнения другойrunnable
горутины. - Экономия CPU: Благодаря этому механизму, ожидающая горутина не потребляет ресурсы CPU (в отличие от активного ожидания в цикле).
- Пробуждение: Горутина будет оставаться в состоянии
waiting
до тех пор, пока другая горутина не отправит данные в этот канал. Как только данные появятся, планировщик переместит заблокированную горутину обратно в очередьrunnable
, и она сможет продолжить свое выполнение, прочитав отправленное значение.
Пример блокирующего чтения:
func main() {
ch := make(chan int) // Создаем небуферизованный канал
go func() {
fmt.Println("Горутина начала работу, сейчас заснет...")
val := <-ch // <-- Горутина блокируется здесь
fmt.Printf("Горутина проснулась и получила значение: %dn", val)
}()
time.Sleep(2 * time.Second) // Даем время горутине запуститься и заблокироваться
fmt.Println("Основная горутина сейчас отправит данные в канал.")
ch <- 10 // <-- Отправка данных разблокирует другую горутину
time.Sleep(1 * time.Second) // Ждем, чтобы горутина успела вывести сообщение
}
Важные случаи:
-
Чтение из закрытого канала: Если канал был закрыт (
close(ch)
) и в нем больше нет данных, операция чтения не будет блокироваться. Она немедленно вернет нулевое значение для типа канала иfalse
в качестве второго параметра.val, ok := <-ch // Если ch закрыт и пуст, вернется (0, false) для chan int
Цикл
for range
над каналом в этом случае просто завершится. -
Неблокирующее чтение: Чтобы избежать блокировки, можно использовать конструкцию
select
с веткойdefault
.select { case val := <-ch: fmt.Printf("Получено значение: %vn", val) default: fmt.Println("Канал пуст, выполняем другие действия.") // Блокировки не произошло }