Как правильно завершить выполнение нескольких горутин в Go?
Sobes Copilot
Получайте ответы в реальном времени
Ответ от нейросети
sobes.tech AI
Существуют несколько распространенных способов:
-
Использование контекста (Context): Рекомендуемый подход для отмены операций в древовидной структуре вызовов. Контекст может передавать сигнал отмены через канал.
package main import ( "context" "fmt" "time" ) func worker(ctx context.Context, id int) { for { select { case <-ctx.Done(): fmt.Printf("Горутина %d завершается: %v\n", id, ctx.Err()) return default: fmt.Printf("Горутина %d работает...\n", id) time.Sleep(1 * time.Second) } } } func main() { ctx, cancel := context.WithCancel(context.Background()) go worker(ctx, 1) go worker(ctx, 2) time.Sleep(3 * time.Second) cancel() // Отправляем сигнал отмены time.Sleep(1 * time.Second) // Даем время горутинам завершиться fmt.Println("Основная программа завершена.") } -
Использование каналов: Отправив сигнал (например, пустую структуру
struct{}) по специальному каналу, можно сообщить горутине о необходимости завершения.package main import ( "fmt" "time" ) func worker(stopChan <-chan struct{}, id int) { for { select { case <-stopChan: fmt.Printf("Горутина %d завершается по каналу\n", id) return default: fmt.Printf("Горутина %d работает...\n", id) time.Sleep(1 * time.Second) } } } func main() { stopChan := make(chan struct{}) go worker(stopChan, 1) go worker(stopChan, 2) time.Sleep(3 * time.Second) close(stopChan) // Закрытие канала сигнализирует о завершении time.Sleep(1 * time.Second) // Даем время горутинам завершиться fmt.Println("Основная программа завершена.") } -
Using
sync.WaitGroup(для ожидания завершения, а не для сигнализации):WaitGroupиспользуется для ожидания завершения группы горутин, но сам по себе не предоставляет механизма остановки. Его часто применяют в сочетании с контекстом или каналами.package main import ( "context" "fmt" "sync" "time" ) func worker(ctx context.Context, wg *sync.WaitGroup, id int) { defer wg.Done() // Уменьшаем счетчик WaitGroup при завершении горутины for { select { case <-ctx.Done(): fmt.Printf("Горутина %d завершается\n", id) return default: fmt.Printf("Горутина %d работает...\n", id) time.Sleep(1 * time.Second) } } } func main() { ctx, cancel := context.WithCancel(context.Background()) var wg sync.WaitGroup numWorkers := 2 wg.Add(numWorkers) // Инициализируем WaitGroup for i := 1; i <= numWorkers; i++ { go worker(ctx, &wg, i) } time.Sleep(3 * time.Second) cancel() // Сигнал отмены wg.Wait() // Ждем завершения всех горутин fmt.Println("Основная программа завершена.") }
Выбор методаDepends on the specific use case and the complexity of the goroutine interactions. Context is generally preferred for cascading cancellations.