Middle
How to properly terminate the execution of multiple goroutines in Go?
sobes.tech AI
Answer from AI
There are several common methods:
-
Using context: Recommended approach for canceling operations in a call tree. Context can pass a cancellation signal through a channel.
package main import ( "context" "fmt" "time" ) func worker(ctx context.Context, id int) { for { select { case <-ctx.Done(): fmt.Printf("Goroutine %d is terminating: %v\n", id, ctx.Err()) return default: fmt.Printf("Goroutine %d is working...\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() // Send cancellation signal time.Sleep(1 * time.Second) // Allow goroutines to finish fmt.Println("Main program finished.") } -
Using channels: Send a signal (e.g., an empty struct
struct{}) through a dedicated channel to notify a goroutine to terminate.package main import ( "fmt" "time" ) func worker(stopChan <-chan struct{}, id int) { for { select { case <-stopChan: fmt.Printf("Goroutine %d is terminating via channel\n", id) return default: fmt.Printf("Goroutine %d is working...\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) // Closing the channel signals termination time.Sleep(1 * time.Second) // Allow goroutines to finish fmt.Println("Main program finished.") } -
Using
sync.WaitGroup(for waiting for completion, not for signaling):WaitGroupis used to wait for a group of goroutines to finish, but does not provide a stop mechanism itself. It is often used in conjunction with context or channels.package main import ( "context" "fmt" "sync" "time" ) func worker(ctx context.Context, wg *sync.WaitGroup, id int) { defer wg.Done() // Decrement WaitGroup counter when goroutine finishes for { select { case <-ctx.Done(): fmt.Printf("Goroutine %d is terminating\n", id) return default: fmt.Printf("Goroutine %d is working...\n", id) time.Sleep(1 * time.Second) } } } func main() { ctx, cancel := context.WithCancel(context.Background()) var wg sync.WaitGroup numWorkers := 2 wg.Add(numWorkers) // Initialize WaitGroup for i := 1; i <= numWorkers; i++ { go worker(ctx, &wg, i) } time.Sleep(3 * time.Second) cancel() // Cancellation signal wg.Wait() // Wait for all goroutines to finish fmt.Println("Main program finished.") }
The choice of method depends on the specific use case and the complexity of goroutine interactions. Context is generally preferred for cascading cancellations.