Что происходит при попытке закрыть nil канал в Go
Что проверяют
Знаете ли вы правила каналов наизусть и понимаете ли, что паника здесь не случайность, а сигнал о сломанном владении каналом. Вопрос короткий, но по ответу видно, писали ли вы конкурентный код или только читали про него.
Как отвечать
Прямо: close для nil-канала вызывает панику close of nil channel. Это не ошибка компиляции и не тихий отказ, программа падает в рантайме.
Соберите поведение nil-канала целиком, обычно ждут именно свод:
- отправка блокирует навсегда;
- чтение блокирует навсегда;
- закрытие роняет панику.
Вечная блокировка это не только источник дедлоков, но и приём: ветка select с nil-каналом никогда не выбирается, поэтому исчерпанный канал занулением отключают.
Рядом назовите соседние паники, их спрашивают следом: повторное закрытие даёт close of closed channel, отправка в закрытый канал даёт send on closed channel, а чтение из закрытого сразу возвращает нулевое значение и ok == false.
Закончите правилом владения: канал закрывает отправитель, и только если он один. Закрытие это сообщение получателям, что данных больше не будет. Когда отправителей несколько, берут sync.WaitGroup и закрывают канал после ожидания, а для отмены сверху используют context.
Где спотыкаются
Защищаются проверкой if ch != nil. От гонки она не спасает, а сам nil говорит, что канал забыли создать в конструкторе, чинить надо там. Проверка прячет настоящую ошибку до ближайшего боевого запуска.
Второе: закрывают канал на стороне получателя или сразу в нескольких горутинах. Первая же гонка даёт close of closed channel, причём падение случится не там, где ошибка, а в произвольной горутине под нагрузкой.
Третье: рассчитывают перехватить панику снаружи. recover работает только в отложенном вызове той же горутины, где паника случилась, наверх она не всплывает и роняет процесс целиком. И даже там, где перехват возможен, продолжать работу после порчи общего состояния обычно хуже честного падения.