Типичные ошибки при работе с channels
Каналы в Go выглядят простой конструкцией:
ch <- value
value := <-ch
Но в реальных проектах именно вокруг channels появляется огромное количество зависаний, deadlock и утечек goroutine.
Ниже ошибки, которые чаще всего встречаются у новичков и регулярно всплывают на собеседованиях.
Использовать time.Sleep вместо нормальной синхронизации
Плохой пример:
go worker()
time.Sleep(time.Second)
Новички часто пытаются подождать, пока горутина завершится через
Sleep.
Проблема в том, что:
- goroutine может завершиться раньше
- может завершиться позже
- код становится нестабильным
- появляются случайные баги
Правильный подход:
- WaitGroup
- channels
- context
- synchronization primitives
Читать через range, но не закрыть channel
for v := range ch {
fmt.Println(v)
}
range завершится только после закрытия channel. Если channel никто не закроет — goroutine зависнет навсегда.
Это одна из самых популярных причин deadlock.
Закрывать channel со стороны receiver
Обычно channel закрывает sender. Потому что именно sender знает — данных больше не будет.
Если receiver закроет channel слишком рано, sender может попытаться записать туда данные.
И программа упадёт:
panic: send on closed channel
Писать в закрытый channel
close(ch)
ch <- 1
После
close channel больше не принимает значения.
Это всегда panic.
Поэтому в конкурентном коде важно понимать:
- кто отвечает за close
- когда канал закрывается
- кто ещё может писать данные
Делать бесконечные goroutine без cancellation
go func() {
for {
value := <-ch
fmt.Println(value)
}
}()
Если sender исчезнет, goroutine останется ждать данные вечно — так появляются goroutine leak. В production это может постепенно убить сервис.
Используй:
- context.Context
- select
- ctx.Done()
- корректное закрытие channels
Игнорировать buffered/unbuffered поведение
Новички часто думают, что channel то же, что очередь. Но небуферизированный channel работает как точка синхронизации.
Sender и receiver встречаются одновременно. А buffered channel позволяет временно складывать значения в буфер. Если этого не понимать, появляются неожиданные блокировки, deadlock и странное поведение pipeline.
Забывать кто читает из channel
Очень частая production-проблема:
- sender пишет данные
- receiver уже завершился
- goroutine зависает на отправке
Особенно часто это ломает:
- pipeline
- worker pool
- fan-in/fan-out
Поэтому при работе с каналами всегда задавай себе три вопроса:
- кто пишет?
- кто читает?
- кто всё это остановит?