> Как используется WaitGroup для ожидания завершения горутин в Go (Go)
Уровень: senior · Роль: backend · Категория: Технические вопросы
Компании: Т-Банк, Моторика
Стек: Go
> Пример ответа
Короткий ответ
sync.WaitGroup - это счётчик горутин, которые нужно дождаться. Метод Add(n) увеличивает счётчик, Done() уменьшает на единицу, а Wait() блокирует вызывающую горутину, пока счётчик не станет нулевым. Используется для синхронизации завершения группы горутин, например, при параллельной обработке задач. Важно вызывать Add до запуска горутины, а Done - через defer, чтобы гарантировать уменьшение счётчика даже при панике.
Подробное объяснение
WaitGroup работает как атомарный счётчик. Основные методы:
Add(delta int)- увеличивает счётчик на delta. Обычно вызывается перед запуском горутины.Done()- эквивалентAdd(-1), вызывается в конце работы горутины.Wait()- блокирует выполнение до тех пор, пока счётчик не станет равным нулю.
Ключевые правила:
Addдолжен вызываться доWait, иначе возможна гонка или deadlock.Addлучше вызывать в основной горутине доgo func(), а не внутри самой горутины - это гарантирует, что счётчик будет корректно установлен до начала ожидания.Doneрекомендуется вызывать черезdefer, чтобы гарантировать уменьшение счётчика даже при панике внутри горутины.WaitGroupнельзя копировать после использования - передавайте указатель.
Типичный паттерн:
GOvar wg sync.WaitGroupfor i := 0; i < n; i++ {wg.Add(1)go func() {defer wg.Done()// работа}()}wg.Wait()
На практике
В реальном коде WaitGroup часто комбинируют с каналами или контекстом для ограничения конкурентности. Например, при пуле воркеров:
GOjobs := make(chan int)var wg sync.WaitGroupfor i := 0; i < 3; i++ {wg.Add(1)go worker(jobs, &wg)}for j := 0; j < 10; j++ {jobs <- j}close(jobs)wg.Wait()
Важно помнить, что WaitGroup не отменяет горутины - он только ждёт их завершения. Для отмены используйте context.Context в связке с select.
Пример кода
GOpackage mainimport ("fmt""sync""time")func main() {var wg sync.WaitGroupfor i := 1; i <= 5; i++ {wg.Add(1)go func(id int) {defer wg.Done()time.Sleep(time.Duration(id) * 100 * time.Millisecond)fmt.Printf("горутина %d завершена\n", id)}(i)}wg.Wait()fmt.Println("все горутины завершены")}
Как отвечать на собеседовании
Начните с определения и назначения. Затем опишите три метода и порядок их использования. Обязательно упомяните правило: Add до запуска горутины, Done через defer. Приведите простой пример с циклом. Если спросят про ограничения - скажите, что WaitGroup не умеет отменять горутины и не передаёт ошибки, для этого есть errgroup из golang.org/x/sync. Также упомяните, что WaitGroup нельзя копировать после использования - это частая ошибка.
Что проверяет интервьюер
- Понимание базовой механики счётчика.
- Знание правильного порядка вызова
AddиWait. - Осознание важности
defer wg.Done(). - Понимание ограничений
WaitGroupи альтернатив (errgroup, каналы,context). - Умение объяснить, почему
Addнельзя вызывать внутри горутины при конкурентном доступе.
Типичные ошибки
- Вызов
Addвнутри горутины - гонка данных и возможный deadlock. - Забытый
Done- программа зависнет наWait. - Копирование
WaitGroup- неопределённое поведение. - Использование
WaitGroupдля передачи данных между горутинами - для этого есть каналы. - Ожидание завершения горутин без обработки паник - если горутина паникует без
defer, счётчик не уменьшится.
> Похожие задачи по backend
Где в памяти живут глобальные переменные в Go?
Когда происходит копирование элементов при передаче массива или слайса в функцию в Go
Для чего используются NoSQL решения?
Где используется recover в Go?
> ГОТОВЫ К СЛЕДУЮЩЕМУ СОБЕСЕДОВАНИЮ?
Запустите тренировочную сессию с ИИ и получите детальную обратную связь, чтобы увереннее проходить реальные интервью