Почему вызов wg.Add(1) внутри каждой горутины — это ошибка и где он должен быть?
Эта программа запускает 5 горутин и ждёт их через WaitGroup, но часто ничего не печатает и завершается до того, как горутины успеют выполниться.
func main() {
var wg sync.WaitGroup
for i := 0; i < 5; i++ {
go func() {
wg.Add(1)
defer wg.Done()
fmt.Println("work")
}()
}
wg.Wait()
}
Найдите и исправьте ошибку.
wg.Add(1) вызывается внутри горутины, но планировщик может не запустить ни одну горутину до того, как main дойдёт до wg.Wait(). Если Wait видит нулевой счётчик, он сразу возвращается, и программа может выйти до запуска горутин. Исправление — вызывать wg.Add(1) в цикле перед запуском каждой горутины.
- ✗Вызывать
wg.Add(1)внутри горутины, а не перед её запуском - ✗Считать, что планировщик запустит горутины до того, как
mainдойдёт доwg.Wait() - ✗Думать, что ошибка в отложенном
Done, а не в запоздаломAdd
- →Почему добавление в WaitGroup до
goустанавливает нужный happens-before сWait? - →Что документация
Add/Waitговорит о конкурентном вызовеAdd?
Найдите ошибку
func main() {
var wg sync.WaitGroup
for i := 0; i < 5; i++ {
go func() {
wg.Add(1) // ОШИБКА: Add внутри горутины
defer wg.Done()
fmt.Println("work")
}()
}
wg.Wait()
}
Почему это ошибка
wg.Add(1) стоит внутри горутины. Но go лишь планирует запуск — нет гарантии, что хоть одна горутина начнёт выполняться до того, как main дойдёт до wg.Wait().
Если планировщик ещё не запустил горутины, счётчик WaitGroup всё ещё 0, поэтому wg.Wait() сразу возвращается, и main завершается — программа может выйти, не напечатав ничего. Вдобавок Add и Wait, выполняясь конкурентно, образуют гонку на счётчике.
✅ Исправление — вызывать Add в цикле до запуска горутины, когда main ещё гарантированно выполняется один:
for i := 0; i < 5; i++ {
wg.Add(1) // до go
go func() {
defer wg.Done()
fmt.Println("work")
}()
}
wg.Wait()