package main import ( "context" "fmt" "os" "os/signal" "sync" "syscall" "time" ) // worker simulates a long-running background task. func worker(ctx context.Context, id int, wg *sync.WaitGroup) { defer wg.Done() fmt.Printf("Worker %d: Started ", id) for { select { case <-ctx.Done(): // Check if the context has been cancelled. fmt.Printf("Worker %d: Shutting down gracefully... %s ", id, ctx.Err()) // Perform cleanup here if necessary time.Sleep(200 * time.Millisecond) // Simulate cleanup time fmt.Printf("Worker %d: Cleaned up and exited. ", id) return default: // Simulate doing some work fmt.Printf("Worker %d: Doing work... ", id) time.Sleep(500 * time.Millisecond) } } } func main() { fmt.Println("Application started. 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 worker(ctx, i, &wg) } // Set up a channel to listen for OS interrupt signals. sigCh := make(chan os.Signal, 1) signal.Notify(sigCh, syscall.SIGINT, syscall.SIGTERM) // Block until a signal is received. <-sigCh fmt.Println(" Received shutdown signal. Initiating graceful shutdown...") // Cancel the context, signaling all goroutines to stop. cancel() // Wait for all goroutines to finish their cleanup. done := make(chan struct{}) go func() { wg.Wait() close(done) }() // Set a timeout for the graceful shutdown process. select { case <-done: fmt.Println("All workers shut down successfully.") case <-time.After(3 * time.Second): fmt.Println("Timeout: Not all workers shut down gracefully within the allowed time.") } fmt.Println("Application exited.") }