Last active
May 30, 2024 02:57
-
-
Save pteich/c0bb58b0b7c8af7cc6a689dd0d3d26ef to your computer and use it in GitHub Desktop.
Example for using go's sync.errgroup together with signal detection signal.NotifyContext to stop all running goroutines
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package main | |
import ( | |
"context" | |
"errors" | |
"fmt" | |
"os/signal" | |
"syscall" | |
"time" | |
"golang.org/x/sync/errgroup" | |
) | |
func main() { | |
ctx, done := signal.NotifyContext(context.Background(), syscall.SIGINT, syscall.SIGTERM, syscall.SIGKILL) | |
defer done() | |
g, gctx := errgroup.WithContext(ctx) | |
// just a ticker every 2s | |
g.Go(func() error { | |
ticker := time.NewTicker(2 * time.Second) | |
i := 0 | |
for { | |
i++ | |
if i > 10 { | |
return nil | |
} | |
select { | |
case <-ticker.C: | |
fmt.Println("ticker 2s ticked") | |
case <-gctx.Done(): | |
fmt.Println("closing ticker 2s goroutine") | |
return gctx.Err() | |
} | |
} | |
}) | |
// just a ticker every 1s | |
g.Go(func() error { | |
ticker := time.NewTicker(1 * time.Second) | |
i := 0 | |
for { | |
i++ | |
if i > 10 { | |
return nil | |
} | |
select { | |
case <-ticker.C: | |
fmt.Println("ticker 1s ticked") | |
case <-gctx.Done(): | |
fmt.Println("closing ticker 1s goroutine") | |
return gctx.Err() | |
} | |
} | |
}) | |
// force a stop after 15s | |
time.AfterFunc(15*time.Second, func() { | |
fmt.Println("force finished after 15s") | |
done() | |
}) | |
// wait for all errgroup goroutines | |
err := g.Wait() | |
if err != nil { | |
if errors.Is(err, context.Canceled) { | |
fmt.Println("context was canceled") | |
} else { | |
fmt.Printf("received error: %v\n", err) | |
} | |
} else { | |
fmt.Println("finished clean") | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Currently there is the
NotifyContext
, which makes it even simpler because you don't have to create a separate goroutine for propagating the signal to context cancellation: https://pkg.go.dev/os/signal#NotifyContext