Sobes.tech
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

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

  1. Внутри цикла for _, u := range users запускается горутина, но переменная u не передаётся в замыкание, что приведёт к гонке данных и неправильной работе.

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

  3. Канал ошибок ch заполняется, но нигде не читается, что может привести к блокировке.

  4. Контекст ctxWithCancel создаётся, но cancel() не вызывается, что может привести к утечкам.

  5. Функция возвращает 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().
  • Ошибки собираются из канала и возвращаются.

Это обеспечит корректную параллельную обработку и правильное управление контекстом и ошибками.