Как остановить чтение из канала в Go?

Ответ

В Go можно остановить чтение из канала (горутину-получателя) несколькими идиоматическими способами:

  1. Закрытие канала отправителем. Это самый распространенный способ. Цикл for range по каналу автоматически завершится, когда канал будет закрыт.

    // Горутина-отправитель
    close(ch)
    
    // Горутина-получатель
    for val := range ch { // Цикл завершится, когда канал ch закроется
        // ... обработка val
    }
  2. Использование select с каналом context.Done(). Это лучший способ для управления жизненным циклом горутин, особенно в серверных приложениях. Отмена контекста сигнализирует горутине о необходимости завершиться.

    func worker(ctx context.Context, ch <-chan int) {
        for {
            select {
            case val := <-ch:
                fmt.Println("Обработка", val)
            case <-ctx.Done(): // Контекст был отменен
                fmt.Println("Получен сигнал завершения")
                return
            }
        }
    }
  3. Использование отдельного "стоп"-канала. Классический паттерн, который использовался до широкого распространения context.

    func worker(ch <-chan int, done <-chan struct{}) {
        for {
            select {
            case val := <-ch:
                fmt.Println("Обработка", val)
            case <-done: // Получен сигнал из стоп-канала
                fmt.Println("Получен сигнал завершения")
                return
            }
        }
    }
    // В другом месте: close(done)

Важно: При чтении из канала всегда можно проверить, был ли он закрыт, с помощью второй переменной ok:

val, ok := <-ch
if !ok {
    // Канал закрыт и пуст. val будет нулевым значением для своего типа.
}