From a7178c67dca2c569cf8a3ae0ecbac9c9d692455d Mon Sep 17 00:00:00 2001 From: devlights Date: Tue, 10 Nov 2020 15:07:44 +0000 Subject: [PATCH] Add time_tick_and_ticker.go Fixes devlights/try-golang#298 --- examples/basic/times/README.md | 1 + examples/basic/times/examples.go | 1 + examples/basic/times/time_tick_and_ticker.go | 91 ++++++++++++++++++++ 3 files changed, 93 insertions(+) create mode 100644 examples/basic/times/time_tick_and_ticker.go diff --git a/examples/basic/times/README.md b/examples/basic/times/README.md index c895aaf6..82260340 100644 --- a/examples/basic/times/README.md +++ b/examples/basic/times/README.md @@ -9,4 +9,5 @@ |time\_unix\_to\_time.go|time\_unix\_to\_time|time.Unix(sec, nsec) のサンプルです.| |time\_now.go|time\_now|time.Now() のサンプルです.| |time\_parse.go|time\_parse|time.Parse() のサンプルです.| +|time\_tick\_and\_ticker.go|time\_tick\_and\_ticker|time.Tick と time.NewTicker の利用シーンの違いについてのサンプルです| diff --git a/examples/basic/times/examples.go b/examples/basic/times/examples.go index 28db9a91..650ece50 100644 --- a/examples/basic/times/examples.go +++ b/examples/basic/times/examples.go @@ -20,4 +20,5 @@ func (r *register) Regist(m mappings.ExampleMapping) { m["time_unix_to_time"] = TimeUnixToTime m["time_now"] = TimeNow m["time_parse"] = TimeParse + m["time_tick_and_ticker"] = TickAndTicker } diff --git a/examples/basic/times/time_tick_and_ticker.go b/examples/basic/times/time_tick_and_ticker.go new file mode 100644 index 00000000..441c887e --- /dev/null +++ b/examples/basic/times/time_tick_and_ticker.go @@ -0,0 +1,91 @@ +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 +}