Ответ
Основное правило в Go: закрывать канал должна та горутина, которая в него пишет (отправитель). Никогда не закрывайте канал со стороны читающей горутины.
Почему именно отправитель?
-
Предотвращение паники: Попытка отправить значение в уже закрытый канал вызывает панику (
panic: send on closed channel). Отправитель точно знает, когда он закончил отправлять данные, и может безопасно закрыть канал. Читатель же никогда не знает, не собирается ли отправитель послать что-то еще. -
Сигнал о завершении: Закрытие канала — это сигнал для всех читающих горутин о том, что данных больше не будет. Это позволяет читателям корректно завершить свою работу.
Как читатель узнает, что канал закрыт?
Читающая горутина может использовать две формы для проверки состояния канала:
-
Цикл
for range: Этот цикл автоматически прекращается, когда канал закрывается и в нем не остается данных.for value := range ch { // Обработка value } // Цикл завершится, когда ch будет закрыт -
Проверка второго значения: При чтении из канала можно получить второй параметр
ok(типаbool). Еслиokравноfalse, это значит, что канал закрыт и данных в нем больше нет.value, ok := <-ch if !ok { // Канал закрыт, выходим }
Что делать, если писателей несколько?
Если в один канал пишут несколько горутин, закрывать его должен тот, кто координирует их работу. Обычно для этого используют sync.WaitGroup, чтобы дождаться завершения всех писателей, и только после этого закрыть канал в отдельной горутине.
func main() {
var wg sync.WaitGroup
ch := make(chan int)
// Запускаем несколько писателей
for i := 0; i < 3; i++ {
wg.Add(1)
go func(id int) {
defer wg.Done()
ch <- id * 10
}(i)
}
// Горутина, которая ждет завершения всех писателей и закрывает канал
go func() {
wg.Wait()
close(ch)
}()
// Читатель
for val := range ch {
fmt.Println(val)
}
} Ответ 18+ 🔞
А, слушай, смотри, тут есть одно железобетонное правило в Go, которое в голове выжечь надо, как татуировку на подъезде: закрывать канал должна та горутина, которая в него пишет, то есть отправитель, ёпта! Никогда, слышишь, никогда не лезь закрывать канал с той стороны, где его читают. Это как зайти в чужой дом и выключить свет, пока хозяин ещё готовит ужин — пиздец наступит моментально.
А че так, а не иначе?
-
Паника, блядь! Представь: ты читаешь, решил, что всё, и закрыл канал. А отправитель в это время уже замахнулся, чтобы кинуть туда ещё одно значение. БАМ!
panic: send on closed channel. Программа ложится, как муха от дихлофоса. Отправитель же сам знает, когда он последний раз плюнул в эту трубу, ему и карты в руки — безопасно заткнуть её. -
Это сигнал, ёбта! Закрытый канал — это не просто дырка в никуда. Это официальное уведомление для всех, кто слушает: «Всё, ребята, передач больше не будет, расходимся». Читатели благодаря этому могут красиво и без истерик завершиться.
А как читателю понять, что всё кончилось?
Да элементарно, Ватсон! Есть два классических приёма:
-
Цикл
for range: Самый элегантный способ. Он работает, пока канал открыт, и сам, как умный, прекратится, когда канал закроют и все данные из него выпьют.for value := range ch { // Делаем что-то с value } // Сюда попадём, только когда ch закроют -
Проверка по-стариковски: Берёшь значение из канала и смотришь вторым глазом. Если второе значение —
false, значит канал приказал долго жить, и новых плюшек не будет.value, ok := <-ch if !ok { // Всё, пипец, канал закрыт. Пора на выход. }
А если писателей, как говна за баней?
Ну, классика жанра! Если в одну трубу плюют несколько горутин, то закрывать её должен самый главный распорядитель. Обычно для этого берут sync.WaitGroup, чтобы дождаться, пока все писатели отстреляются, и только потом, в отдельной горутине, с чистой совестью захлопнуть канал.
func main() {
var wg sync.WaitGroup
ch := make(chan int)
// Запускаем толпу писателей
for i := 0; i < 3; i++ {
wg.Add(1)
go func(id int) {
defer wg.Done()
ch <- id * 10 // Плюнул и свободен
}(i)
}
// А это наш могильщик. Ждёт, пока все писатели умрут, и закрывает канал.
go func() {
wg.Wait()
close(ch) // Вот теперь можно!
}()
// Читатель спокойно пьёт, пока есть что.
for val := range ch {
fmt.Println(val)
}
}
Вот и вся магия. Запомни — писатель закрывает, читатель просто идёт домой, когда бар опустел. Не усложняй, и будет тебе счастье, а не паника.