概要
小ネタ。知らなかったので忘れないうちにメモメモ。
time.Tick
は、特定のインターバルで処理をする場合にとても便利。
指定時間の間、特定のインターバルで処理を繰り返すようなものが書きたい場合は
time.Tick
と time.After
使えばサクっとできるので好きです。
package main import ( "log" "os" "time" ) func main() { var ( interval = 1 * time.Second appLog = log.New(os.Stdout, "[tick] ", 0) ) var ( tick <-chan time.Time timeover <-chan time.Time ) tick = time.Tick(interval) timeover = time.After(5 * time.Second) LOOP: for { select { case t := <-tick: appLog.Println(t.UTC().Unix()) case <-timeover: break LOOP } } }
time.Tick
も time.After
のどちらも <-chan time.Time
を返してくれます。
time.After
のドキュメントをみたときに
The underlying Timer is not recovered by the garbage collector until the timer fires.
って書いてあったので、time.Tick
も同じ感じだろうって勝手に思っていました。
time.Tickは goroutine leak する
別のタイミングで、time.Tick
のドキュメントみたら、以下のように書いてあってビックリ。
While Tick is useful for clients that have no need to shut down the Ticker, be aware that without a way to shut it down the underlying Ticker cannot be recovered by the garbage collector; it "leaks".
思いっきり リーク するって書いてるやん・・。
よくよく考えたらそりゃそうだってなりました。time.After
と違って、止め時がないですもんね。
ドキュメントにある通り、以下のように使い分けるのがいいみたいです。
- インターバル処理がアプリケーションの生存期間と同じであれば
time.Tick
で構わない - 一時的な時間でインターバル処理するような場合は
time.NewTicker
使って*time.Ticker
経由で処理する
サンプル
package times import ( "time" "github.com/devlights/gomy/output" ) // TickAndTicker -- time.Tick と time.NewTicker の利用シーンの違いについてのサンプルです。 func TickAndTicker() error { // ------------------------------------------------- // time.Tick と time.NewTicker の使い分け // // time.Tick は、以下の定義となっている。 // func Tick(d time.Duration) <-chan Time // 受信専用のチャネルを返しているので、内部で goroutine を // 起動してチャネル経由で値を返してきている。 // 受信専用のチャネルであるので、このチャネルをユーザ側で // クローズすることは出来ない。なので、Tickを呼び出した際に // 生成される goroutine は止まることが無い。 // 止まるタイミングがなく、ずっと動いている goroutine は // メインゴルーチン以外は goroutine leak していると考える。 // // なので、time.Tick のドキュメントには以下のように記載されている。 // While Tick is useful for clients that have no need to shut down the Ticker, // be aware that without a way to shut it down the underlying // Ticker cannot be recovered by the garbage collector; it "leaks". // // time.Tick で生成される goroutine は終了しないので // アプリケーションの生存期間と同じ時間生存できるタイミングで // 利用する場合は便利である。 // // それ以外のケース、例えば 特定の時間枠で処理する goroutineの // 中で利用したい場合は、time.NewTicker で明示的に time.Ticker を // 生成して利用するべき。time.Tickerには Stop メソッドが用意されている // ので、それを呼び出すと内部リソースが開放される。 // (time.Ticker.C のチャネルはクローズされないことに注意) // ------------------------------------------------- // 一時的な処理時間で動作するゴルーチン done := func() <-chan struct{} { done := make(chan struct{}) go func() { defer close(done) // このような場合は time.Tick ではなく time.NewTicker を使うべき ticker := time.NewTicker(500 * time.Millisecond) timeout := time.After(2 * time.Second) defer ticker.Stop() LOOP: for { select { case t := <-ticker.C: output.Stdoutl("[goroutine] ", t.UTC().Unix()) case <-timeout: break LOOP } } }() return done }() // ここはメインゴルーチン // ここで処理が終わるまでインターバルする場合などに利用する場合は // time.Tick は便利(アプリがそのまま終了するので goroutine leak は問題にならない) var ( tick <-chan time.Time timeout = time.After(5 * time.Second) ) LOOP: for { select { case <-done: // 非同期処理が終わったのでメインの出力に切り替え // 再びこのチャネルが select で選択されないように nil を設定しておく tick = time.Tick(500 * time.Millisecond) done = nil output.Stdoutl("[main ]", "goroutine end.") case t := <-tick: output.Stdoutl("[main ]", t.UTC().Unix()) case <-timeout: break LOOP } } return nil }
try-golang/time_tick_and_ticker.go at master · devlights/try-golang · GitHub
実行すると以下のようになります。
$ make run go run github.com/devlights/try-golang/cmd/trygolang -onetime -example "" ENTER EXAMPLE NAME: time_tick [Name] "time_tick_and_ticker" [goroutine] 1605243230 [goroutine] 1605243230 [goroutine] 1605243231 [goroutine] 1605243231 [main ] goroutine end. [main ] 1605243232 [main ] 1605243232 [main ] 1605243233 [main ] 1605243233 [main ] 1605243234 [Elapsed] 5.00018683s
おすすめ書籍
自分が読んだGo関連の本で、いい本って感じたものです。
- 作者:Katherine Cox-Buday
- 発売日: 2018/10/26
- メディア: 単行本(ソフトカバー)
- 作者:松尾 愛賀
- 発売日: 2016/04/15
- メディア: 単行本(ソフトカバー)
プログラミング言語Go (ADDISON-WESLEY PROFESSIONAL COMPUTING SERIES)
- 作者:Alan A.A. Donovan,Brian W. Kernighan
- 発売日: 2016/06/20
- メディア: 単行本(ソフトカバー)
過去の記事については、以下のページからご参照下さい。
- いろいろ備忘録日記まとめ
サンプルコードは、以下の場所で公開しています。
- いろいろ備忘録日記サンプルソース置き場