Код-ревью Middle Каналы и select спрашивают часто обновлено

Код-ревью Go: воркеры, закрытие канала и select с default

Вопрос

Функция раздаёт задачи четырём воркерам. Найдите ошибки работы с каналами.

func Process(items []string) {
    jobs := make(chan string)
    done := make(chan bool)
    for i := 0; i < 4; i++ {
        go worker(jobs, done)
    }
    for _, it := range items {
        jobs <- it
    }
    for i := 0; i < 4; i++ {
        <-done
    }
}

func worker(jobs chan string, done chan bool) {
    for {
        select {
        case j := <-jobs:
            handle(j)
        default:
        }
        if len(jobs) == 0 {
            close(jobs)
            done <- true
            return
        }
    }
}

Коротко

Правильная схема: отправитель закрывает канал, воркеры читают через range, ожидание через WaitGroup.

Что должен найти ревьюер

  1. Производительностьстрока 20
    select с пустым default превращает цикл в активное ожидание: воркер крутит процессор на 100%, пока задач нет.
  2. Ошибкастрока 22
    len небуферизованного канала всегда 0: воркер выйдет после первой же итерации, даже если задачи ещё будут. len канала нельзя использовать для синхронизации.
  3. Ошибкастрока 23
    Канал закрывает получатель, и делают это четыре воркера: второй close вызовет panic: close of closed channel, а отправка в main в закрытый канал вызовет panic: send on closed channel.
  4. Ошибкастрока 8
    Если все воркеры уже вышли, отправка заблокируется навсегда: дедлок. Канал должен закрывать отправитель после цикла отправки.
  5. Стильстрока 15
    Направленные типы <-chan string и chan<- bool не дали бы воркеру закрыть канал задач: компилятор поймал бы ошибку.

Разбор

Правильная схема: отправитель закрывает канал, воркеры читают через range, ожидание через WaitGroup.

func Process(items []string) {
    jobs := make(chan string)
    var wg sync.WaitGroup
    for range 4 {
        wg.Add(1)
        go func() {
            defer wg.Done()
            for j := range jobs {
                handle(j)
            }
        }()
    }
    for _, it := range items {
        jobs <- it
    }
    close(jobs)
    wg.Wait()
}

Ответьте сами, а не прочитайте

В тренажёре вопрос попадёт в повторения и вернётся, когда начнёт забываться. Плюс билеты с зачётом: 20 вопросов, не больше 2 ошибок.