Что происходит с горутиной, если она пытается записать в канал после ошибки?
Что проверяют
Понимаете ли вы, что происходит на отправке, когда сценарий пошёл не по счастливому пути: канал уже закрыт или читателя больше нет. Это вопрос про паники, про утечки горутин и про то, кто в вашем коде отвечает за закрытие канала.
Как отвечать
Исхода два, и их стоит разделить.
- Канал закрыт:
ch <- vвызывает паникуsend on closed channel. Паника не локальна для горутины в том смысле, что её не перехватят снаружи: безrecoverв той же самой горутине падает весь процесс. - Канал открыт, но читателя уже нет, потому что вызывающий ушёл по ошибке или таймауту: отправка в небуферизованный канал блокируется навсегда. Процесс жив, горутина висит вечно, это утечка.
Проверить перед записью, открыт ли канал, нельзя: такой операции в языке нет, а проверка через чтение это гонка. Работают другие приёмы:
- закрывает писатель, а не читатель; при нескольких писателях канал либо не закрывают вовсе, либо закрывают из координатора после
wg.Wait(); - отправку с отменой пишут через
select; - для единственного ответа проще буфер на одно значение,
make(chan res, 1): отправка не заблокируется, даже если ответ уже никому не нужен.
select {
case ch <- res:
case <-ctx.Done():
return ctx.Err()
}Где спотыкаются
Ставят recover в вызывающей функции и думают, что перехватят панику из запущенной горутины. Не перехватят: recover действует только в кадре своей горутины, так что защищать приходится каждую, и обычно это признак того, что владение каналом продумано плохо.
Второе: пытаются опросить состояние канала перед записью. Между проверкой и ch <- v канал успевают закрыть, и код падает ровно в тот один раз из тысячи, который не воспроизводится на отладке.
Третье: на пути ошибки делают ранний return, не отправив ничего, а читатель ждёт ровно одно значение. Помогает отправка через отложенный вызов или буферизованный канал, но чаще правильный ответ это errgroup, где результат возвращается обычным return err.