golangのよくあるtickerのサンプルがイケてない件
golangでのtickerのサンプルで以下のようにgoroutine内でrangeを使ってforループを回すのをよく見かけます。
package main import "time" import "fmt" func main() { ticker := time.NewTicker(10 * time.Millisecond) go func() { for t := range ticker.C { fmt.Println("Tick at", t) } }() time.Sleep(35 * time.Millisecond) ticker.Stop() }
コレ、以下のようにちょっと変更して実行してみましょう。
package main import "time" import "fmt" func main() { ticker := time.NewTicker(10 * time.Millisecond) go func() { for t := range ticker.C { fmt.Println("Tick at", t) } fmt.Println("Reachable?") }() time.Sleep(35 * time.Millisecond) ticker.Stop() time.Sleep(100 * time.Millisecond) }
"Reachable?"とは表示してくれませんね?
tickerのStop()してもチャンネルをcloseしてはくれないので、残念ながらこのforループは抜けることはないのです。 その結果、goroutineがリークしてしまいます。
んじゃあどうすればいいかっつーと、以下のようにする必要があります。
package main import "time" import "fmt" func main() { ticker := time.NewTicker(10 * time.Millisecond) stop := make(chan bool) go func() { loop: for { select { case t := <-ticker.C: fmt.Println("Tick at", t) case <-stop: break loop } } fmt.Println("Reachable!") }() time.Sleep(35 * time.Millisecond) ticker.Stop() close(stop) time.Sleep(100 * time.Millisecond) }
……別でチャンネル用意したりとか正直クソメンドクサイですね。
ただ単にバックグラウンドでの繰り返し処理をするためだけに毎回こんなコード書くとか耐えられません。
ということで、ラッパーをつくりました。
https://github.com/okzk/ticker
このラッパーを使えば以下のようになります。
package main import ( "fmt" "github/okzk/ticker" "time" ) func main() { ticker := ticker.New(10*time.Millisecond, func(t time.Time) { fmt.Println("Tick at", t) }) time.Sleep(35 * time.Millisecond) ticker.Stop() }
ラクチン!