Ответ
Для ожидания завершения группы горутин в Go используется стандартный и идиоматичный инструмент — sync.WaitGroup
.
WaitGroup
— это, по сути, счетчик активных горутин. Если main
завершится раньше, чем дочерние горутины, они будут принудительно остановлены. WaitGroup
позволяет main
блокироваться до тех пор, пока все задачи не будут выполнены.
Принцип работы sync.WaitGroup
:
wg.Add(n)
: Увеличивает счетчикWaitGroup
наn
. Обычно вызывается перед запуском каждой горутины (wg.Add(1)
).wg.Done()
: Уменьшает счетчик на 1. Этот вызов должен быть в конце работы горутины. Часто используется сdefer
, чтобы гарантировать выполнение, даже если в горутине произойдет паника.wg.Wait()
: Блокирует выполнение той горутины, в которой он вызван (обычно вmain
), до тех пор, пока счетчикWaitGroup
не станет равен нулю.
Пример:
package main
import (
"fmt"
"sync"
"time"
)
func worker(id int, wg *sync.WaitGroup) {
// defer гарантирует, что Done() будет вызван при выходе из функции
defer wg.Done()
fmt.Printf("Worker %d startingn", id)
time.Sleep(time.Second) // Симуляция работы
fmt.Printf("Worker %d donen", id)
}
func main() {
// Создаем экземпляр WaitGroup
var wg sync.WaitGroup
for i := 1; i <= 5; i++ {
// Увеличиваем счетчик перед запуском горутины
wg.Add(1)
// Запускаем горутину, передавая указатель на wg
go worker(i, &wg)
}
fmt.Println("Main: Waiting for workers to finish...")
// Ожидаем, пока счетчик не обнулится
wg.Wait()
fmt.Println("Main: All workers have finished. Exiting.")
}
Важно: Использование каналов для этой цели также возможно (например, каждая горутина отправляет сигнал в канал по завершении), но sync.WaitGroup
является более простым, производительным и предназначенным именно для этой задачи решением.