Горутины
Конкурентность встроена в Go на уровне языка: запустить параллельную задачу можно одним ключевым словом go. Это вводная тема: её цель — понять, что делает go, почему горутина «лёгкая» и как корректно дождаться завершения группы горутин, не углубляясь в устройство планировщика.
За кажущейся простотой стоят несколько правил, которые и проверяют на junior-секции. Горутина — это лёгкий поток, которым управляет рантайм Go, а не операционная система: их можно запускать десятками тысяч, потому что стартовый стек крошечный и растёт по мере надобности. Но когда main возвращается, программа завершается и все оставшиеся горутины обрываются — поэтому за ними нужно явно ждать. Канонический способ — sync.WaitGroup: Add до запуска, Done в каждой горутине, Wait блокирует, пока счётчик не обнулится. Обмен данными между горутинами — через каналы — это отдельная тема Каналы; устройство планировщика и паттерны конкурентности — в теме Конкурентность.
Карта темы
- Ключевое слово
go—go f()запускает горутину — лёгкий поток под управлением рантайма; она работает конкурентно, а возврат изmainобрывает все горутины разом. sync.WaitGroup—Add(n)до запуска,Done()(обычно черезdefer) в каждой горутине,Wait()блокирует до обнуления счётчика — канонический способ дождаться группы горутин.
Частые ошибки и ловушки
| Ошибка | Последствие |
|---|---|
Запустить go f() и сразу вернуться из main | Программа завершится, горутина не успеет отработать — вывода не будет |
| Считать горутину обычным потоком ОС | Потоков ОС тысячи не создашь; горутина дешёвая, ей управляет рантайм, а не ядро |
Вызвать wg.Add(1) внутри уже запущенной горутины | Wait() может увидеть нулевой счётчик и выйти раньше — гонка; Add делают до go |
Забыть wg.Done() в одной из горутин | Счётчик не дойдёт до нуля, Wait() зависнет навсегда |
Передавать WaitGroup по значению, а не указателем | Каждая горутина увидит свою копию счётчика — Wait не дождётся; передавайте *sync.WaitGroup |
Значение для собеседований
Горутины — почти обязательное начало junior-секции по Go: с них стартуют, потому что это и есть «фишка» языка. Глубокого знания планировщика на этом уровне не ждут, но базовую механику запуска и ожидания спрашивают всегда.
Что обычно проверяют:
- Что делает
go f()и почему горутина «лёгкая» — ей управляет рантайм Go, а не ОС. - Почему программа из
go fmt.Println(...)вmainчасто не печатает ничего (возврат изmainобрывает горутины). - Как дождаться нескольких горутин через
sync.WaitGroupи почемуAddставят доgo. - Что произойдёт, если забыть
Doneили передатьWaitGroupпо значению.