LoginSignup
0
0

More than 5 years have passed since last update.

goroutineあれこれ4

Posted at

お題

気まぐれでgoroutineと戯れる。。。

関連記事Index

開発環境

# OS

$ cat /etc/os-release 
NAME="Ubuntu"
VERSION="18.04.1 LTS (Bionic Beaver)"

# 言語

$ go version
go version go1.11.4 linux/amd64

実践

OSシグナルを受け付けて複数goroutineを停止

複数のgoroutineを生成実行し、メインgoroutineはOSシグナルを待ち受ける。
OSシグナルから受信があると、実行中の複数goroutineすべてに停止通知を行う。

■OSシグナルの受け手

OSシグナルを受信したら、あらかじめ受け取っておいたgoroutine停止用のキャンセル通知関数notifyToStopを実行する。
これにより、事前に実行していた複数goroutineに”停止”を通知する。
受け取るOSシグナルはSIGINTだけ。(とりあえずCtrl+Cでキーボード割り込みした事例で良いので)

[signal_receiver.go]
type signalReceiver struct {
    ch           chan os.Signal // OSからのシグナル受信用チャネル
    notifyToStop func()         // OSからのシグナル受信時の他チャネルへの停止通知関数
}

func newSignalReceiver(notifyToStop func()) *signalReceiver {
    ch := make(chan os.Signal, 1)
    signal.Notify(ch, syscall.SIGINT)
    return &signalReceiver{
        ch:           ch,
        notifyToStop: notifyToStop,
    }
}

func (r *signalReceiver) wait() {
    <-r.ch
    fmt.Println("received signal !!!!!")
    r.notifyToStop()
}

■goroutine実行対象の関数

受け取ったnameを1秒おきに出力し続けるだけの関数。
コンテキストから終了通知を受け取ったら、処理終了。

[main.go]
// 1秒おきにnameを出力
func echoLangName(ctx context.Context, name string) {
    for {
        select {
        case <-ctx.Done():
            fmt.Println("***** stop echo " + strings.TrimSpace(name) + " *****")
            return
        default:
            fmt.Println(name)
            time.Sleep(1 * time.Second)
            continue
        }
    }
}

■main関数

5言語(選択は適当)分のgoroutineを生成したら、あとはメインgoroutineでOSシグナル受信待ち。

[main.go]
func main() {
    // エコー対象の言語は5つ
    langs := []string{
        "Go",
        "    Java",
        "         C++",
        "              PHP",
        "                  Ruby"}

    ctx, cancelFunc := context.WithCancel(context.Background())

    for _, lang := range langs {
        go echoLangName(ctx, lang)
    }

    rec := newSignalReceiver(cancelFunc)
    rec.wait()

    time.Sleep(3 * time.Second) // 本来はWaitGroup使う
}

実行

開始から2秒くらい経ったところで Ctrl+C で割り込み。
すると、OSシグナルからの割り込みを待っていた wait() 関数にて「received signal !!!!!」を出力。
その後、あらかじめ用意しておいたキャンセル通知関数の実行により、main以外の各goroutineが停止。

output.gif

参考

今回載せた動画GIFは↓を参考に作ってみました。使い勝手もいいし、できたGIFも軽い。
https://qiita.com/syohex/items/d7076828c54114eb7657

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0