Проверяет понимание работы каналов в Go, особенно поведения range по каналу без закрытия и потенциальной блокировки горутин.
В Go каналы используются для синхронизации и передачи данных между горутинами. Оператор range по каналу позволяет последовательно читать значения, пока канал не будет закрыт. Если канал не закрыт, range продолжает ждать новые данные, блокируя выполнение горутины.
Предположим, есть горутина-писатель, которая отправляет несколько значений в канал, и горутина-читатель, которая использует range для чтения. Если писатель завершится, не закрыв канал, читатель останется в состоянии ожидания, так как range не знает, что данные закончились. Это приведет к блокировке (deadlock), если все горутины находятся в ожидании и нет других активных горутин.
package main
import "fmt"
func main() {
ch := make(chan int)
// Писатель
go func() {
for i := 0; i < 5; i++ {
ch <- i
}
// Не закрываем канал
}()
// Читатель
for v := range ch {
fmt.Println(v)
}
// Программа зависнет здесь, так как канал не закрыт
}В этом примере после отправки пяти значений писатель завершается, но канал не закрыт. Читатель продолжает ждать, и программа блокируется, вызывая deadlock.
Чтобы избежать блокировки, писатель должен закрыть канал после завершения отправки данных. Это сигнализирует range, что больше значений не будет, и цикл завершится.
package main
import "fmt"
func main() {
ch := make(chan int)
go func() {
defer close(ch)
for i := 0; i < 5; i++ {
ch <- i
}
}()
for v := range ch {
fmt.Println(v)
}
// Вывод: 0 1 2 3 4
}Понимание этого поведения критично при проектировании конкурентных систем, особенно при работе с пулами воркеров, обработке потоков данных или реализации паттернов producer-consumer. Неправильное управление закрытием каналов может привести к утечкам горутин и зависанию приложений.
Используйте close(ch) в горутине-писателе, когда все данные отправлены, чтобы корректно завершить цикл range в читателе. Это гарантирует отсутствие блокировок и чистую синхронизацию между горутинами.