sync.WaitGroup⏳В прошлый раз остановились на неприятном: горутину запустили, а
main закончился раньше, чем она успела хоть что-то сделать. Сегодня про то, как это лечится по-человечески.Первое, что приходит в голову — заткнуть дыру задержкой.
time.Sleep(time.Second)Работает ровно до того дня, когда сеть притормозит и секунды не хватит. А если поставить запас побольше, программа будет честно стоять на месте даже тогда, когда всё давно готово. Гадание, а не синхронизация.
Нормальный инструмент называется
sync.WaitGroup. По сути это счётчик горутин, которых мы ждём.var wg sync.WaitGroup
for _, city := range cities {
wg.Add(1)
go func(city string) {
defer wg.Done()
fetchWeather(city)
}(city)
}
wg.Wait()
Три метода, и на этом всё.
Add(1) — добавили горутину, которую надо дождаться. Done() — эта закончила, счётчик минус один. Wait() — стоим здесь, пока счётчик не обнулится.Приятная мелочь:
var wg sync.WaitGroup уже готов к работе, никаких New и инициализаций. Нулевое значение и есть рабочее, в Go так устроено много чего.defer перед wg.Done() тоже не для красоты. Если внутри горутины случится паника или ранний return, Done без defer просто не вызовется, счётчик никогда не дойдёт до нуля, и Wait будет ждать вечно. Причём программа не упадёт с ошибкой, а именно зависнет — отдельное удовольствие ловить такое в проде.Ну и главная ловушка, на которой спотыкаются почти все.
Add обязан стоять до go, а не внутри горутины.go func() {
wg.Add(1) // так нельзяdefer wg.Done()
...
}()Выглядит-то логично, горутина сама себя посчитала. Но
main в этот момент бежит дальше и вполне может добраться до Wait раньше, чем горутина вообще стартует и что-то прибавит. Счётчик ноль, Wait решает, что ждать некого, программа идёт дальше без результатов — то есть мы вернулись ровно к той проблеме, с которой начинали, только теперь она замаскирована правильно выглядящим кодом.Как выглядит счётчик по шагам, почему порядок завершения горутин не важен, и что делать, когда горутин заранее неизвестно сколько — всё это в модуле, там же задания, где это надо написать руками и посмотреть, как оно себя ведёт.
Напоминаю, что скидка 20% на курс PRO Go Для продвинутых всё ещё действует, но остаётся всего два дня❤️