Back to questions
Middle+
3
func Do(ctx context.Context, users []User) (map[string]int64, error) { var mu sync.Mutex var wg sync.WaitGroup ch := make(chan error, len(users)) names := make(map[string]int64, 0) ctxWithCancel, cancel := context.WithCancel(ctx) wg.Add(len(users)) for _, u := range users { go func() { defer wg.Done() select { case name, err := fetch(ctx, u): if err != nil { ch <- err return } mu.Lock() names[name] = names[name] + 1 mu.Unlock() case <-ctxWithCancel.Err: } }() } return names, nil }
Companies where asked
Wildberries & Russ Bank
Answer from AI
sobes.tech AI
В представленном коде есть несколько проблем:
-
Внутри цикла
for _, u := range usersзапускается горутина, но переменнаяuне передаётся в замыкание, что приведёт к гонке данных и неправильной работе. -
В
selectиспользуетсяcase name, err := fetch(ctx, u):, но такой синтаксис вselectневозможен —selectработает с каналами, а не с функциями. -
Канал ошибок
chзаполняется, но нигде не читается, что может привести к блокировке. -
Контекст
ctxWithCancelсоздаётся, ноcancel()не вызывается, что может привести к утечкам. -
Функция возвращает
namesиnilсразу, не дожидаясь завершения горутин.
Исправленный пример:
func Do(ctx context.Context, users []User) (map[string]int64, error) {
var mu sync.Mutex
var wg sync.WaitGroup
ch := make(chan error, len(users))
names := make(map[string]int64)
ctxWithCancel, cancel := context.WithCancel(ctx)
defer cancel()
wg.Add(len(users))
for _, u := range users {
u := u // локальная копия для замыкания
go func() {
defer wg.Done()
name, err := fetch(ctxWithCancel, u)
if err != nil {
ch <- err
cancel() // отменяем остальные операции
return
}
mu.Lock()
names[name]++
mu.Unlock()
}()
}
wg.Wait()
close(ch)
for err := range ch {
if err != nil {
return nil, err
}
}
return names, nil
}
Основные изменения:
- Создана локальная копия
uдля каждой горутины. - Убрано использование
selectс функцией, вызовfetchпроисходит напрямую. - Добавлен вызов
cancel()при ошибке. - Добавлено ожидание завершения всех горутин через
wg.Wait(). - Ошибки собираются из канала и возвращаются.
Это обеспечит корректную параллельную обработку и правильное управление контекстом и ошибками.