close и ok
Шаг разбора закончил пакет и закрыл свой канал — следующий шаг сразу понял, что файлов больше не будет. Миша записал в заметки простое правило: канал закрывал тот, кто в него писал. С этого момента каждый шаг точно знал, когда партия закончилась.
закрытие канала — сигнал «данных больше не будет»: приём из закрытого сначала отдаёт буферизованные значения с ok == true, после опустошения буфера — zero value и ok == false; отправка в закрытый и повторное закрытие паникуют, а закрывает канал отправитель.
Закрытие как сигнал конца
ch := make(chan File)
go func() { // шаг-производитель
for _, f := range batch {
ch <- f
}
close(ch) // файлов больше не будет
}()
for {
f, ok := <-ch
if !ok {
break
}
process(f)
}
close(ch) закрывает канал. Приём из закрытого канала не блокируется: если в буфере остались значения, он сначала отдаёт их с ok == true, и только после опустошения буфера возвращает zero value и ok == false. Поэтому ok == true означает «получено реально отправленное значение», а ok == false — «канал закрыт и буфер пуст».
С буферизованным каналом это особенно заметно — zero value придёт не сразу, а только после остатков:
ch := make(chan int, 1)
ch <- 7
close(ch)
v1, ok1 := <-ch // 7, true — остаток буфера
v2, ok2 := <-ch // 0, false — буфер пуст, канал закрыт
Что нельзя делать с закрытым каналом
- Отправка в закрытый канал — паника.
- Закрытие закрытого канала — паника.
- Приём из закрытого — безопасен: сначала остатки буфера с
ok == true, затем zero value сok == false.
ch := make(chan int)
close(ch)
ch <- 1 // panic: send on closed channel
close(ch) // panic: close of closed channel
Кто закрывает
Каноническое правило: закрывает отправитель. Логика проста — он знает, когда данные закончились. Получателю закрывать канал неестественно: если канал читают несколько горутин, одна из них может закрыть его под отправкой другой — паника.
Есть тонкость с несколькими отправителями: если значения в один канал шлют несколько горутин, закрытие из одной из них опасно — другая может отправить в уже закрытый канал. Решение — выделить закрытие в отдельную горутину, которая знает, когда все отправители закончили (WaitGroup), либо не закрывать канал вовсе и использовать другой механизм завершения. Вопрос «кто закрывает» стоит задавать при проектировании каждого канала — и каждого шага конвейера.
Вы узнаете:
- как закрытие небуферизованного канала сигналит «сделано»
- как избегать паники на отправке в закрытый при нескольких отправителях
close — сигнал конца от отправителя; приём из закрытого сначала отдаёт остатки буфера с ok=true, затем zero value с ok=false; отправка в закрытый и повторное закрытие — паника.
10 000 ₽ · 60 минут · онлайн