Ответ
Идиоматичный и безопасный способ управления жизненным циклом нескольких горутин в Go — использование пакета context
.
Основной подход: context.WithCancel
и sync.WaitGroup
context.Context
используется для сигнализации горутинам о необходимости завершения. Создается родительский контекст с функцией отмены (cancel
).sync.WaitGroup
используется для того, чтобы основная горутина дождалась фактического завершения всех дочерних горутин после отправки сигнала отмены.
Это позволяет реализовать graceful shutdown (плавное завершение).
Пример:
func worker(ctx context.Context, wg *sync.WaitGroup, id int) {
defer wg.Done()
fmt.Printf("Воркер %d запущенn", id)
select {
case <-ctx.Done(): // Ожидаем сигнала отмены от контекста
fmt.Printf("Воркер %d получил сигнал завершения.n", id)
return
case <-time.After(5 * time.Second): // Имитация работы
fmt.Printf("Воркер %d завершил работу штатно.n", id)
}
}
func main() {
var wg sync.WaitGroup
// Создаем контекст, который можно отменить
ctx, cancel := context.WithCancel(context.Background())
for i := 1; i <= 3; i++ {
wg.Add(1)
go worker(ctx, &wg, i)
}
// Имитируем необходимость срочно завершить все горутины через 1 секунду
time.Sleep(1 * time.Second)
fmt.Println("Отправляем сигнал отмены всем воркерам...")
cancel() // Отправляем сигнал
wg.Wait() // Ожидаем, пока все горутины вызовут wg.Done()
fmt.Println("Все воркеры завершены. Программа выключается.")
}
Аварийное завершение (panic
)
Использование panic()
приводит к каскадному завершению всех горутин и остановке программы (если panic
не перехвачен с помощью recover
). Это не способ управления, а механизм реакции на невосстановимую ошибку. Не используйте panic
для штатного завершения горутин.