|
| 1 | +package times |
| 2 | + |
| 3 | +import ( |
| 4 | + "time" |
| 5 | + |
| 6 | + "github.com/devlights/gomy/output" |
| 7 | +) |
| 8 | + |
| 9 | +// TickAndTicker -- time.Tick と time.NewTicker の利用シーンの違いについてのサンプルです。 |
| 10 | +func TickAndTicker() error { |
| 11 | + // ------------------------------------------------- |
| 12 | + // time.Tick と time.NewTicker の使い分け |
| 13 | + // |
| 14 | + // time.Tick は、以下の定義となっている。 |
| 15 | + // func Tick(d time.Duration) <-chan Time |
| 16 | + // 受信専用のチャネルを返しているので、内部で goroutine を |
| 17 | + // 起動してチャネル経由で値を返してきている。 |
| 18 | + // 受信専用のチャネルであるので、このチャネルをユーザ側で |
| 19 | + // クローズすることは出来ない。なので、Tickを呼び出した際に |
| 20 | + // 生成される goroutine は止まることが無い。 |
| 21 | + // 止まるタイミングがなく、ずっと動いている goroutine は |
| 22 | + // メインゴルーチン以外は goroutine leak していると考える。 |
| 23 | + // |
| 24 | + // なので、time.Tick のドキュメントには以下のように記載されている。 |
| 25 | + // While Tick is useful for clients that have no need to shut down the Ticker, |
| 26 | + // be aware that without a way to shut it down the underlying |
| 27 | + // Ticker cannot be recovered by the garbage collector; it "leaks". |
| 28 | + // |
| 29 | + // time.Tick で生成される goroutine は終了しないので |
| 30 | + // アプリケーションの生存期間と同じ時間生存できるタイミングで |
| 31 | + // 利用する場合は便利である。 |
| 32 | + // |
| 33 | + // それ以外のケース、例えば 特定の時間枠で処理する goroutineの |
| 34 | + // 中で利用したい場合は、time.NewTicker で明示的に time.Ticker を |
| 35 | + // 生成して利用するべき。time.Tickerには Stop メソッドが用意されている |
| 36 | + // ので、それを呼び出すと内部リソースが開放される。 |
| 37 | + // (time.Ticker.C のチャネルはクローズされないことに注意) |
| 38 | + // ------------------------------------------------- |
| 39 | + |
| 40 | + // 一時的な処理時間で動作するゴルーチン |
| 41 | + done := func() <-chan struct{} { |
| 42 | + done := make(chan struct{}) |
| 43 | + go func() { |
| 44 | + defer close(done) |
| 45 | + |
| 46 | + // このような場合は time.Tick ではなく time.NewTicker を使うべき |
| 47 | + ticker := time.NewTicker(500 * time.Millisecond) |
| 48 | + timeout := time.After(2 * time.Second) |
| 49 | + defer ticker.Stop() |
| 50 | + |
| 51 | + LOOP: |
| 52 | + for { |
| 53 | + select { |
| 54 | + case t := <-ticker.C: |
| 55 | + output.Stdoutl("[goroutine] ", t.UTC().Unix()) |
| 56 | + case <-timeout: |
| 57 | + break LOOP |
| 58 | + } |
| 59 | + } |
| 60 | + }() |
| 61 | + |
| 62 | + return done |
| 63 | + }() |
| 64 | + |
| 65 | + // ここはメインゴルーチン |
| 66 | + // ここで処理が終わるまでインターバルする場合などに利用する場合は |
| 67 | + // time.Tick は便利(アプリがそのまま終了するので goroutine leak は問題にならない) |
| 68 | + var ( |
| 69 | + tick <-chan time.Time |
| 70 | + timeout = time.After(5 * time.Second) |
| 71 | + ) |
| 72 | + |
| 73 | +LOOP: |
| 74 | + for { |
| 75 | + select { |
| 76 | + case <-done: |
| 77 | + // 非同期処理が終わったのでメインの出力に切り替え |
| 78 | + // 再びこのチャネルが select で選択されないように nil を設定しておく |
| 79 | + tick = time.Tick(500 * time.Millisecond) |
| 80 | + done = nil |
| 81 | + |
| 82 | + output.Stdoutl("[main ]", "goroutine end.") |
| 83 | + case t := <-tick: |
| 84 | + output.Stdoutl("[main ]", t.UTC().Unix()) |
| 85 | + case <-timeout: |
| 86 | + break LOOP |
| 87 | + } |
| 88 | + } |
| 89 | + |
| 90 | + return nil |
| 91 | +} |
0 commit comments