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

Ответ

Для организации чтения из одного канала несколькими горутинами (паттерн "Competing Consumers" или "Fan-out"), можно запустить несколько горутин, которые читают из этого общего канала в цикле for range.

Go runtime автоматически и конкурентно распределит значения из канала между всеми читающими горутинами. Каждое отправленное в канал значение будет получено только одной горутиной.

Пример:

package main

import (
    "fmt"
    "sync"
    "time"
)

func main() {
    jobs := make(chan int, 100)
    var wg sync.WaitGroup

    // Запускаем 3 воркера (горутины) для чтения
    for i := 1; i <= 3; i++ {
        wg.Add(1)
        go func(id int) {
            defer wg.Done()
            for j := range jobs {
                fmt.Printf("Воркер %d обработал задачу %dn", id, j)
                time.Sleep(time.Millisecond * 500) // Имитация работы
            }
        }(i)
    }

    // Отправляем 10 задач в канал
    for j := 1; j <= 10; j++ {
        jobs <- j
    }
    close(jobs) // Важно закрыть канал, чтобы циклы for range завершились

    wg.Wait() // Ожидаем завершения всех воркеров
    fmt.Println("Все задачи обработаны.")
}

Ключевые аспекты:

  1. Конкурентное чтение: Операция чтения из канала (<-ch) является блокирующей и атомарной. Когда несколько горутин ожидают на одном канале, только одна из них получит следующее значение.
  2. Закрытие канала: Канал обязательно нужно закрывать (close(ch)) после того, как все данные были в него отправлены. Это является сигналом для циклов for range в горутинах, что данных больше не будет, и они могут безопасно завершиться. В противном случае произойдет deadlock.
  3. Недетерминированное распределение: Нет гарантии, что данные будут распределяться между горутинами поочередно или в каком-либо предсказуемом порядке. Распределение зависит от внутреннего планировщика Go.
  4. Синхронизация: Часто вместе с этим паттерном используется sync.WaitGroup, чтобы главная горутина могла дождаться завершения работы всех дочерних горутин.