Sobes.tech
Back to questions
Middle+
3

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: } }() }

Companies where asked
Wildberries & Russ Bank

Answer from AI

sobes.tech AI

В представленном коде есть несколько проблем:

  1. Внутри цикла запускается горутина, но переменная u не передается в замыкание, что приведет к гонке данных и неправильным результатам.

  2. В конструкции select используется case name, err := fetch(ctx, u):, но select не работает с вызовами функций, он ожидает каналы.

  3. Отсутствует вызов cancel() при ошибке, чтобы остановить остальные горутины.

  4. Канал ошибок 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
}

Такой подход корректно обрабатывает ошибки, синхронизирует доступ к карте и отменяет операции при необходимости.