Код-ревью 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.
Что должен найти ревьюер
- Производительностьстрока 20select с пустым default превращает цикл в активное ожидание: воркер крутит процессор на 100%, пока задач нет.
- Ошибкастрока 22
lenнебуферизованного канала всегда 0: воркер выйдет после первой же итерации, даже если задачи ещё будут. len канала нельзя использовать для синхронизации. - Ошибкастрока 23Канал закрывает получатель, и делают это четыре воркера: второй close вызовет panic: close of closed channel, а отправка в main в закрытый канал вызовет panic: send on closed channel.
- Ошибкастрока 8Если все воркеры уже вышли, отправка заблокируется навсегда: дедлок. Канал должен закрывать отправитель после цикла отправки.
- Стильстрока 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 ошибок.