package main import ( "context" "fmt" "os" "os/signal" "sync" "syscall" "time" ) // workerGoroutine simulates a background task that needs to shut down gracefully. func workerGoroutine(ctx context.Context, id int, wg *sync.WaitGroup) { defer wg.Done() fmt.Printf("Worker %d started. ", id) for { select { case <-time.After(500 * time.Millisecond): // Simulate doing some work fmt.Printf("Worker %d doing work... ", id) case <-ctx.Done(): // Received a shutdown signal fmt.Printf("Worker %d received shutdown signal. Cleaning up... ", id) time.Sleep(1 * time.Second) // Simulate cleanup time fmt.Printf("Worker %d cleaned up and exiting. ", id) return } } } func main() { fmt.Println("Application starting. Press Ctrl+C to initiate graceful shutdown.") // Create a context that can be cancelled ctx, cancel := context.WithCancel(context.Background()) var wg sync.WaitGroup // Start multiple worker goroutines for i := 1; i <= 3; i++ { wg.Add(1) go workerGoroutine(ctx, i, &wg) } // Set up a channel to listen for OS signals (e.g., Ctrl+C) sigChan := make(chan os.Signal, 1) signal.Notify(sigChan, syscall.SIGINT, syscall.SIGTERM) // Block until a signal is received sig := <-sigChan fmt.Printf(" Received OS signal: %s. Initiating graceful shutdown... ", sig) // Cancel the context, which will signal all goroutines to stop cancel() // Wait for all goroutines to finish their cleanup fmt.Println("Waiting for all workers to finish...") done := make(chan struct{}) go func() { wg.Wait() close(done) }() // Set a timeout for graceful shutdown to prevent endless waiting select { case <-done: fmt.Println("All workers gracefully shut down.") case <-time.After(5 * time.Second): fmt.Println("Timeout for graceful shutdown exceeded. Force quitting.") } fmt.Println("Application exiting.") }