Коллега написал генератор, который отдаёт count сообщений через канал.
На ревью код «работает на одном сообщении», а в проде программа падает с
fatal error: all goroutines are asleep - deadlock!.
func produceMessages(count int) chan string {
out := make(chan string, 1)
for i := 0; i < count; i++ {
out <- fmt.Sprintf("event %d", i+1)
}
return out
}
func main() {
for text := range produceMessages(6) {
fmt.Println("received:", text)
}
}
Объясни, что здесь не так, и исправь produceMessages, не меняя сигнатуру.
Требования
produceMessages(n) возвращает канал, из которого по порядку читаются
"event 1", "event 2", …, "event n".
- После последнего сообщения канал закрыт, поэтому
range у потребителя
завершается. При count <= 0 возвращается сразу закрытый канал.
- Функция возвращается сразу, не дожидаясь, пока потребитель всё прочитает,
и работает при любом
count. Заводить буфер на все count сообщений нельзя:
поток может быть большим.
- После того как потребитель дочитал канал, горутин не остаётся. Код проходит
go test -race.
Пример
for text := range produceMessages(3) {
fmt.Println(text) // event 1, event 2, event 3
}
Потребитель прочитал два сообщения и вышел из цикла. Горутина генератора
при этом навсегда повисает на отправке, и это утечка.
func produceMessagesCtx(ctx context.Context, count int) <-chan string
- Пока
ctx не отменён, поведение такое же, как у produceMessages.
- После отмены
ctx горутина генератора завершается и закрывает канал, даже
если из него больше никто не читает.
Устно: почему буфер ёмкостью count «лечит» дедлок и чем такое решение
плохо? Что вернёт чтение из закрытого канала и как отличить его от обычного
значения?