Go のアプリケーションを作っていると、シグナルの受信に伴い処理を中断したり再起動する処理を実装する事が多い。これまでは signal.Notify
でシグナルをキャッチし、別途 context.WithCancel
で作成したコンテキストを自ら cancel
する処理を書かなければならなかった。Go の tip に入ったコミットにより、これが幾分改善される様になった。
package main
import (
"context"
"fmt"
"os"
"os/signal"
"time"
)
func main() {
ctx, stop := signal.NotifyContext(context.Background(), os.Interrupt)
defer stop()
select {
case <-time.After(time.Second):
fmt.Println("done")
case <-ctx.Done():
stop()
fmt.Println("canceled")
}
}
このコードを実行すると、1秒経過すると done が、途中で CTRL-C をタイプすると canceled が表示される。一見、利用用途が少ない様に見えるが以下の様に goroutine を複数起動し、signal で一括終了する時には便利。
package main
import (
"context"
"fmt"
"os"
"os/signal"
"sync"
)
func blocking(ctx context.Context, wg *sync.WaitGroup) {
defer wg.Done()
fmt.Println("worker started")
<-ctx.Done()
fmt.Println("worker canceled")
}
func main() {
ctx, stop := signal.NotifyContext(context.Background(), os.Interrupt)
defer stop()
var wg sync.WaitGroup
wg.Add(3)
go blocking(ctx, &wg)
go blocking(ctx, &wg)
go blocking(ctx, &wg)
wg.Wait()
}