В чем разница между wg.Add в цикле и за циклом

Ответ

Разница в управлении счетчиком ожидания WaitGroup.

В цикле:

var wg sync.WaitGroup
for _, task := range tasks {
    wg.Add(1) // Увеличиваем счетчик перед запуском каждой горутины
    go func(t Task) {
        defer wg.Done()
        process(t)
    }(task)
}
wg.Wait()

Плюсы:

  • Точно отражает количество запущенных горутин, даже если tasks изменяется динамически.
  • Безопасно, если количество задач неизвестно заранее или определяется в процессе.

За циклом:

var wg sync.WaitGroup
wg.Add(len(tasks)) // Увеличиваем счетчик один раз
for _, task := range tasks {
    go func(t Task) {
        defer wg.Done()
        process(t)
    }(task)
}
wg.Wait()

Плюсы:

  • Меньше накладных расходов (однократный вызов Add вместо N вызовов).
  • Подходит, если количество задач известно и фиксировано до запуска горутин.

Опасность: Если len(tasks) изменится (например, задачи добавляются или удаляются) после вызова wg.Add(len(tasks)), но до запуска всех горутин, счетчик может стать некорректным. Это может привести к зависанию (Wait никогда не вернется) или панике (Done вызван больше раз, чем Add).