Version 1.
package main
import (
"context"
"fmt"
"os"
"os/signal"
"syscall"
"time"
)
func main() {
errs := make(chan error, 2)
workerCtx, ctxDone := context.WithCancel(context.Background())
go func() {
errs <- worker1(workerCtx)
}()
go func() {
errs <- worker2(workerCtx)
}()
// Terminate on CTRL+C
c := make(chan os.Signal, 1)
signal.Notify(c, syscall.SIGINT, syscall.SIGTERM)
<-c
ctxDone()
// Wait for all the go routine to return before terminating completely.
for i := 0; i < cap(errs); i++ {
<-errs
}
}
func worker1(ctx context.Context) error {
for {
select {
// simulate a work that can't be interrupted unless it's done.
default:
fmt.Println("worker 1 working")
time.Sleep(3 * time.Second)
fmt.Println("worker 1 done working")
case <-ctx.Done():
fmt.Println("worker 1 received a shutdown")
time.Sleep(1 * time.Second)
fmt.Println("gracefully terminated worker 1")
return ctx.Err()
}
}
}
func worker2(ctx context.Context) error {
for {
select {
// simulate a work that can't be interrupted unless it's done.
default:
fmt.Println("worker 2 working")
time.Sleep(10 * time.Second)
fmt.Println("worker 2 done working")
case <-ctx.Done():
fmt.Println("worker 2 received a shutdown")
time.Sleep(5 * time.Second)
fmt.Println("gracefully terminated worker 2")
return ctx.Err()
}
}
}
Version 2 using package [oklog/run].
package main
import (
"context"
"fmt"
"os"
"os/signal"
"syscall"
"time"
"github.com/oklog/run"
)
func main() {
g := run.Group{}
workerCtx, ctxDone := context.WithCancel(context.Background())
g.Add(func() error {
return worker1(workerCtx)
}, func(error) {
ctxDone()
})
g.Add(func() error {
return worker2(workerCtx)
}, func(error) {
ctxDone()
})
// // Terminate on CTRL+C
cancelInterrupt := make(chan struct{})
g.Add(func() error {
c := make(chan os.Signal, 1)
signal.Notify(c, syscall.SIGINT, syscall.SIGTERM)
select {
case sig := <-c:
return fmt.Errorf("received signal %s", sig)
case <-cancelInterrupt:
return nil
}
}, func(error) {
close(cancelInterrupt)
})
fmt.Println(g.Run())
}
func worker1(ctx context.Context) error {
for {
select {
// simulate a work that can't be interrupted unless it's done.
default:
fmt.Println("worker 1 working")
time.Sleep(3 * time.Second)
fmt.Println("worker 1 done working")
case <-ctx.Done():
fmt.Println("worker 1 received a shutdown")
time.Sleep(1 * time.Second)
fmt.Println("gracefully terminated worker 1")
return ctx.Err()
}
}
}
func worker2(ctx context.Context) error {
for {
select {
// simulate a work that can't be interrupted unless it's done.
default:
fmt.Println("worker 2 working")
time.Sleep(10 * time.Second)
fmt.Println("worker 2 done working")
case <-ctx.Done():
fmt.Println("worker 2 received a shutdown")
time.Sleep(5 * time.Second)
fmt.Println("gracefully terminated worker 2")
return ctx.Err()
}
}
}