Skip to content

Instantly share code, notes, and snippets.

@pteich
Last active May 30, 2024 02:57
Show Gist options
  • Save pteich/c0bb58b0b7c8af7cc6a689dd0d3d26ef to your computer and use it in GitHub Desktop.
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
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")
}
}
@bjwschaap
Copy link

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

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment