Когда можно закрыть канал при нескольких писателях и читателях в Go?
Что проверяют
Знаете ли вы правило «закрывает тот, кто пишет» и понимаете ли, почему при нескольких отправителях ни один из них не может закрыть канал сам. Следом обычно спрашивают, что будет с писателями, если читатели остановились раньше.
Как отвечать
Начните с того, что делает close:
- отправка в закрытый канал: паника
send on closed channel; - повторный
close: тоже паника, как иcloseу nil-канала; - чтение из закрытого канала: не паникует. Сначала отдаётся остаток буфера, потом нулевое значение и
ok == false, аfor rangeзавершается сам.
Отсюда вывод: закрытие это сигнал читателям «данных больше не будет», и подать его можно, только когда все писатели гарантированно закончили. При нескольких писателях закрытие выносят в отдельную горутину-координатор: писатели делают wg.Done(), координатор ждёт wg.Wait() и один раз вызывает close(ch). wg.Add вызывается до запуска горутины, а не внутри неё. В Go 1.25 появился wg.Go(f), который делает то же самое короче.
Хорошо добавить обратный сценарий. Если остановиться должны писатели (читатель ушёл, запрос отменён), им рассылают отмену через context.Context или отдельный done-канал, а каждая отправка делается в select с case <-ctx.Done(). Канал данных после выхода писателей закрывает координатор, либо его просто забирает сборщик мусора: закрывать канал ради освобождения памяти не нужно.
Если закрытие всё же могут инициировать несколько горутин, его оборачивают в sync.Once, но это скорее признак, что владение каналом не продумано.
Где спотыкаются
Закрывают канал со стороны читателя или «последним писателем», которого вычисляют счётчиком без синхронизации. Любая гонка тут превращается в панику на проде.
Пытаются проверить «закрыт ли канал» перед отправкой. Такой проверки в языке нет, а если бы и была, между проверкой и отправкой состояние успеет измениться.
Забывают про заблокированных писателей: читатель вышел по ошибке, а писатели навсегда повисли на ch <- v. Это утечка горутин, которую лечит только select с отменой.