Goで一定周期で何かを行う方法

  • 74
    いいね
  • 0
    コメント
この記事は最終更新日から1年以上が経過しています。

Goでは言語組み込みの並行処理のサポートがあるから、一定の周期で何らかの処理を行うのは簡単だ。その処理のためのgoroutineを作って、それを一定の間隔で目覚させて実際の処理を行わせるようにすれば良い。

Sleep

一番簡単な方法は無限ループを回して、ループの最後にtime.Sleepで一定時間休むという方法だろう。

go func() {
    for {
        // ここでなにかを行う
        time.Sleep(3 * time.Second) // 3秒休む
    }
}()

この方法は手っ取り早いけど、Sleepを途中で中断することはできない。たとえばこのgoroutineがいらなくなったときにそれをチャネルで通知してgoroutineを終了させるとか、あるいは早めに目覚めさせてループの先頭に復帰させるとか、そういったことはSleepが返ってくるまで行うことができない。

Ticker

「多重化できない」問題はだいたいチャネルで抽象化すればうまく扱うことができる。

この場合、time.Tickerを使うともっと柔軟にループを回すことができる。time.Tickerはランタイムのグローバルなタイマーに関連付けられる値で、ランタイムからTickerの中のチャネルに一定周期で値が送られるようになっている。チャネル経由でクロックが送られてくるので、select文を使って他のチャネルとクロックとを同時に待つことが可能になっている。

go func() {
    t := time.NewTicker(3 * time.Second) // 3秒おきに通知
    for {
        select {
        case <-t.C:
            // 3秒経過した。ここで何かを行う。
        }
    }
    t.Stop() // タイマを止める。
}()

select文のなかにcase節を足すことによってクロック待ち以外に処理を分岐させることができる。

最後にTicker.Stopを呼び忘れるとリソースリークが起こるので注意。ランタイムがtの参照を持ち続けているのでtはGCされないし、そうなるとランタイムはtにずっと値を一定周期で送り続けることになるので、それによってプログラムが間違った動作をしたりはしないかもしれないが、無駄が発生することになってしまう。