正確には goroutine へ直接シグナルを送る方法なんてありませんが、次のパッケージを使うことで似たようなことを実現することができます。(タイトルで釣ってしまって申し訳ないです!!)
context with signal in golang
シグナル受信するための context を作成することができるパッケージです。
あるシグナルをプロセスが受け取ったら context cancel を行う
package main
import (
"context"
"fmt"
"sync"
"syscall"
"github.com/Code-Hex/sigctx"
)
func main() {
ctx := sigctx.WithCancelSignals(
context.Background(),
syscall.SIGINT,
syscall.SIGTERM,
syscall.SIGHUP,
)
var wg sync.WaitGroup
for i := 0; i < 10; i++ {
wg.Add(1)
go func() {
defer wg.Done()
for {
select {
// You can cancel by send signal.
case <-ctx.Done():
fmt.Println(ctx.Err())
return
}
}
}()
}
wg.Wait()
}
sigctx.WithCancelSignals
の第一引数に context.Context
を、その後ろに context キャンセルを行わせたい os.Signal
を指定します。(この場合だと INT, TERM, HUP を受信すると context
キャンセルを行います)
めっちゃシンプルですね!
受け取ったシグナルを context を使っている全ての goroutine へ通知する
package main
import (
"context"
"fmt"
"sync"
"syscall"
"github.com/Code-Hex/sigctx"
)
func main() {
ctx := sigctx.WithSignals(
context.Background(),
syscall.SIGINT,
syscall.SIGTERM,
syscall.SIGHUP,
)
var wg sync.WaitGroup
for i := 0; i < 10; i++ {
wg.Add(1)
go func() {
defer wg.Done()
for {
select {
// You can notify signal received.
case <-sigctx.Recv(ctx):
signal, err := sigctx.Signal(ctx)
if err != nil {
fmt.Println(err.Error())
return
}
switch signal {
case syscall.SIGINT:
fmt.Println(signal.String())
return
default:
fmt.Println(signal.String())
}
case <-ctx.Done():
fmt.Println(ctx.Err())
return
}
}
}()
}
wg.Wait()
}
sigctx.WithSignals
と sigctx.Recv
を使うことで通知が可能になります。
sigctx.Recv
の引数には「sigctx.WithSignals
の返り値の context.Context
」を渡すことで実現可能です。
また受信したシグナルを受け取るには sigctx.Signal
を使うと受け取ることができます。sigctx.Signal
の引数も「sigctx.WithSignals
の返り値の context.Context
」をわたうようにしてください。
この辺はもう少し工夫すればもっとシンプルになりそうですが、もっと良いアイデアお持ちの方がいましたら是非 PR を送っていただけると嬉しいです。
是非使ってみてください!