Что происходит с горутиной и каналом в Go после выполнения функции и закрытия канала
Что проверяют
Знаете ли вы, что закрытие канала это сигнал читателям, а не освобождение ресурса, и что происходит с горутиной после того, как её функция завершилась. Вопрос отсеивает тех, кто выучил close как аналог Close() у файла.
Как отвечать
- Горутина живёт ровно столько, сколько выполняется её функция. Функция вернулась, отработали отложенные вызовы, стек освободил рантайм. Отдельно завершать или собирать горутину не нужно.
- Канал это обычное значение, сборщик мусора заберёт его, когда на него не останется ссылок. Закрывать канал ради освобождения памяти не требуется, закрытие нужно только чтобы сообщить читателям, что данных больше не будет.
- Чтение из закрытого канала не блокируется и не паникует: сначала выдаются оставшиеся буферизованные значения, потом нулевое значение типа элемента. Отличить одно от другого помогает
v, ok := <-ch, гдеok == falseозначает, что канал закрыт и пуст. for v := range chна закрытом канале штатно завершает цикл, это основной способ дождаться конца потока данных.- Закрывает тот, кто пишет, и ровно один раз: повторный
closeи запись в закрытый канал это паника. - В
selectзакрытый канал всегда готов к чтению, поэтому исчерпанную ветку зануляют (ch = nil), иначе цикл начнёт крутиться вхолостую.
Где спотыкаются
Считают, что чтение из закрытого канала приводит к панике. Паникуют запись и повторное закрытие, а чтение молча отдаёт нули, и как раз поэтому такие нули регулярно уезжают дальше по коду как настоящие данные.
Второе: закрывают канал со стороны читателя или из нескольких писателей сразу. Тогда один закрывает, пока второй ещё отправляет, и получается паника на записи. Если писателей много, канал закрывает координатор после wg.Wait().
Третье: путают «горутина завершилась» и «работа доделана». Если функция вышла досрочно по ошибке и не закрыла канал, читатель повиснет в ожидании, которое никогда не кончится. Ради этого у писателя и ставят defer close(ch).