> Что происходит с горутиной, если она пытается записать в канал после ошибки? (Go)

Уровень: senior · Роль: backend · Язык: Go · Категория: Технические вопросы

Компании: Wildberries

Стек: Go

> Пример ответа

Короткий ответ

Запись в канал после ошибки - это не особая ситуация в Go. Горутина просто выполняет операцию отправки в канал, и её поведение зависит от состояния канала: открыт он или закрыт, есть ли буфер и готов ли получатель. Ошибка как таковая не влияет на канал - важно, что именно произошло: закрытие канала, deadlock или паника в другой горутине. Если канал открыт и есть место или получатель, запись пройдёт успешно. Если канал закрыт - будет паника.

Подробное объяснение

В Go каналы не имеют понятия "ошибка" как состояния. Ошибка - это результат выполнения функции, и она никак не связана с каналом напрямую. Поэтому вопрос нужно разбить на несколько сценариев:

  1. Запись в закрытый канал - это единственный случай, когда горутина гарантированно упадёт с паникой send on closed channel. Паника произойдёт в горутине, которая выполняет запись, и если она не обработана через recover, упадёт вся программа.

  2. Запись в канал без получателя и без буфера - горутина заблокируется навсегда, если нет активного получателя. Это классический deadlock, который runtime обнаружит только если все горутины заблокированы.

  3. Запись в буферизированный канал с заполненным буфером - аналогично блокировке до появления получателя.

  4. Запись в nil канал - блокировка навсегда, без паники.

Важно понимать: ошибка, возвращённая из функции, не меняет состояние канала. Канал - это отдельная сущность. Если горутина получила ошибку и пытается записать результат в канал, она либо заблокируется, либо запишет значение, либо упадёт - в зависимости от состояния канала, а не от наличия ошибки.

На практике часто встречается паттерн, когда после ошибки горутина отправляет в канал структуру с полем err. Это корректно, если канал открыт. Но если канал уже закрыт (например, через close в другом месте), запись вызовет панику.

На практике

В реальном коде важно различать два сценария:

  • Горутина сама обрабатывает ошибку и решает, что делать с каналом. Она может записать ошибку в канал, закрыть канал или просто выйти. Здесь нет ничего специфичного - это обычная логика.

  • Канал закрывается извне, пока горутина ещё пишет. Это частая ошибка в конкурентном коде: одна горутина закрывает канал, а другая продолжает в него писать. В этом случае паника неизбежна.

Рекомендация для senior-уровня: использовать паттерн "владелец канала" - только одна горутина имеет право закрывать канал, и она же отвечает за отправку всех значений. Ошибки передаются через отдельный канал или через структуру с полем err.

Ещё один важный момент: если горутина заблокирована на записи в канал, а канал никто не читает, это приведёт к утечке горутины. Runtime не убьёт такую горутину автоматически, если есть другие активные горутины. Поэтому нужно проектировать так, чтобы канал всегда имел получателя, либо использовать select с context.Done() для возможности выхода.

Пример кода

GO
package main
import (
"fmt"
"time"
)
func worker(ch chan<- int) {
// имитация ошибки
err := fmt.Errorf("something went wrong")
if err != nil {
// запись в открытый канал - ок
ch <- 42
return
}
}
func main() {
ch := make(chan int, 1)
go worker(ch)
time.Sleep(100 * time.Millisecond)
fmt.Println(<-ch)
}

А вот пример паники при записи в закрытый канал:

GO
package main
func main() {
ch := make(chan int)
close(ch)
ch <- 1 // panic: send on closed channel
}

Как отвечать на собеседовании

Начни с уточнения: "Ошибка не влияет на канал напрямую, важно состояние канала". Затем перечисли сценарии: закрытый канал - паника, блокировка - deadlock или утечка, открытый канал - успешная запись. Покажи понимание паттерна владельца канала и упомяни select с context для graceful shutdown.

Если интервьюер спрашивает про конкретный кейс - например, "горутина получила ошибку из БД и пишет в канал" - ответь: "Она запишет значение, если канал открыт и есть получатель или буфер. Если канал закрыт - упадёт. Ошибка сама по себе не меняет поведение канала".

Добавь про утечку горутин: заблокированная запись без получателя - это утечка, и это серьёзная проблема в production.

Что проверяет интервьюер

Интервьюер проверяет:

  • понимание модели памяти и конкурентности в Go;
  • знание состояний канала: открытый, закрытый, nil;
  • умение различать панику, блокировку и успешную операцию;
  • знание паттернов работы с каналами: владелец канала, select, context;
  • способность рассуждать о реальных сценариях, а не заученных определениях.

Для senior-уровня важно показать, что ты не смешиваешь понятие ошибки из бизнес-логики и состояние канала - это разные уровни абстракции.

Типичные ошибки

  • Утверждение "после ошибки горутина падает" - неверно, паника возникает только при записи в закрытый канал.
  • Игнорирование блокировки - кандидат не упоминает, что запись без получателя блокирует горутину навсегда.
  • Путаница с nil каналом - запись в nil канал блокируется, но не паникует.
  • Предложение закрывать канал из нескольких горутин - это гарантированная паника или гонка.
  • Отсутствие упоминания select с context - для senior это обязательный инструмент при работе с каналами в долгоживущих сервисах.

> ГОТОВЫ К СЛЕДУЮЩЕМУ СОБЕСЕДОВАНИЮ?

Запустите тренировочную сессию с ИИ и получите детальную обратную связь, чтобы увереннее проходить реальные интервью