Какая горутина должна закрывать канал и почему?

Ответ

Основное правило в Go: закрывать канал должна та горутина, которая в него пишет (отправитель). Никогда не закрывайте канал со стороны читающей горутины.

Почему именно отправитель?

  1. Предотвращение паники: Попытка отправить значение в уже закрытый канал вызывает панику (panic: send on closed channel). Отправитель точно знает, когда он закончил отправлять данные, и может безопасно закрыть канал. Читатель же никогда не знает, не собирается ли отправитель послать что-то еще.

  2. Сигнал о завершении: Закрытие канала — это сигнал для всех читающих горутин о том, что данных больше не будет. Это позволяет читателям корректно завершить свою работу.

Как читатель узнает, что канал закрыт?

Читающая горутина может использовать две формы для проверки состояния канала:

  1. Цикл for range: Этот цикл автоматически прекращается, когда канал закрывается и в нем не остается данных.

    for value := range ch {
        // Обработка value
    }
    // Цикл завершится, когда ch будет закрыт
  2. Проверка второго значения: При чтении из канала можно получить второй параметр 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)
    }
}