Какие способы общения между goroutine существуют в Go
Что проверяют
Понимаете ли вы модель конкурентности Go: каналы для передачи данных и владения ими, sync для защиты общего состояния, context для отмены. Следом почти всегда спрашивают, что будет при записи в закрытый канал и как найти утечку горутин.
Как отвечать
Начните с принципа «не общайтесь через общую память, делитесь памятью через общение», затем покажите инструменты:
- небуферизованный канал:
make(chan T), отправитель ждёт получателя, это передача данных и синхронизация сразу; - буферизованный канал:
make(chan T, n), отправка блокируется при полном буфере. Годится как очередь задач или семафор; closeиrange: закрывает канал отправитель, получатели выходят изfor v := range ch. Формаv, ok := <-chотличает закрытый канал от нулевого значения;select: ожидание на нескольких каналах,defaultделает операцию неблокирующей,ctx.Done()илиtime.Afterдают таймаут. Если готовы несколько веток, выбор случайный;context.Context: отмена и дедлайн для дерева горутин;sync.Mutexиsync.RWMutex: когда состояние проще защитить, чем передавать, например кэш;sync/atomic:atomic.Int64,atomic.Boolдля счётчиков и флагов;sync.WaitGroupиerrgroup: ожидание группы горутин,errgroup.WithContextещё и отменяет остальных при первой ошибке. С Go 1.25 уWaitGroupесть методGo;sync.Once: однократная инициализация.
Правило выбора: передаёте данные, берите канал; защищаете структуру, к которой ходят многие, берите мьютекс.
Где спотыкаются
Не знают граничных случаев. Запись в закрытый канал и повторный close дают панику, операции с nil-каналом блокируются навсегда.
Оставляют горутины без остановки. Горутина ждёт канал, который никто уже не читает, и висит до конца программы. Лечится через context, ловится goleak в тестах или профилем горутин в pprof.
Делают всё на каналах из принципа, даже счётчик, и получают код медленнее и сложнее мьютекса. Сюда же пропуск go test -race, который ловит гонки, незаметные в обычных тестах.