Назад к вопросам
Middle+
4
func fetch(ctx context.Context, u User) (string, error) { // что-то делаем по сети time.Sleep(time.Millisecond * 10) // имитация задержки return u.Name, nil //return "", errors.New("some error") } 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: } }() }
Компании, где спрашивали
Wildberries & Russ Bank
Ответ от нейросети
sobes.tech AI
В представленном коде есть несколько проблем:
-
Внутри цикла запускается горутина, но переменная
uне передается в замыкание, что приведет к гонке данных и неправильным результатам. -
В конструкции
selectиспользуетсяcase name, err := fetch(ctx, u):, ноselectне работает с вызовами функций, он ожидает каналы. -
Отсутствует вызов
cancel()при ошибке, чтобы остановить остальные горутины. -
Канал ошибок
chне используется для возврата ошибки из функцииDo.
Правильный подход:
- Передавать
uкак параметр в анонимную функцию. - Вызывать
fetchвнутри горутины, а результат отправлять в канал. - Использовать
context.WithCancelдля отмены всех операций при ошибке. - Собрать результаты после завершения всех горутин.
Пример исправленного кода:
func Do(ctx context.Context, users []User) (map[string]int64, error) {
var mu sync.Mutex
names := make(map[string]int64)
ctxWithCancel, cancel := context.WithCancel(ctx)
defer cancel()
errCh := make(chan error, len(users))
wg := sync.WaitGroup{}
wg.Add(len(users))
for _, u := range users {
u := u // захват переменной
go func() {
defer wg.Done()
name, err := fetch(ctxWithCancel, u)
if err != nil {
errCh <- err
cancel()
return
}
mu.Lock()
names[name]++
mu.Unlock()
}()
}
wg.Wait()
close(errCh)
for err := range errCh {
if err != nil {
return nil, err
}
}
return names, nil
}
Такой подход корректно обрабатывает ошибки, синхронизирует доступ к карте и отменяет операции при необходимости.