My External Storage

Nov 18, 2018 - 2 minute read - Comments - go

[Go] n秒おきになにかするループをtime.Tickで書く #go

Go言語による並行処理に知らない書き方があったのでメモ。

TL;DR

  • time.Tick()for rangeで使うとスマートに無限ループが書ける
  • x秒間だけn秒おきになにかするループはtime.Tick()使わないほうがシンプルな気がする

n秒おきになにかする無限ループ

forループとtim.Sleep()で書くとき

x秒おきに何かをする無限ループを書くときこんなforループを書きがちだった。 (playgroundのサンプルコードは途中で終わるようにカウンタを入れている。)

https://play.golang.org/p/Do7e6enUu5z

package main

import (
	"fmt"
	"time"
)

func main() {
	for {
		fmt.Println("Tick!!")
		time.Sleep(3 * time.Millisecond)
	}
}

time.Tickを使ってX秒おきに無限ループ

time.Tickを使えば以下のようにも書ける。

https://play.golang.org/p/VlWP05XLRGX

package main

import (
	"fmt"
	"time"
)

func main() {
	for range time.Tick(3 * time.Millisecond) {
		fmt.Println("Tick!!")
	}
}

x秒間だけn秒おきになにかする有限ループ

特定の時間の間だけ定期的に何かをするサンプルコードもGo言語による並行処理に載っていた。

https://play.golang.org/p/O4KWXe5KQJJ

package main

import (
	"fmt"
	"time"
)

func main() {
	limit := 5 * time.Millisecond
	// time.Since()で取得したDurationでループを止めるか決める
	for begin := time.Now(); time.Since(begin) < limit; {
		fmt.Println("Tick!!")
		time.Sleep(1 * time.Millisecond)
	}
}

time.Tickを使うとこんな感じ?これは素直に上の書き方をしたほうがシンプルだとおもう。

https://play.golang.org/p/8NxMqbqcmX5

package main

import (
	"fmt"
	"time"
)

func main() {
	limit := 5 * time.Millisecond
	begin := time.Now()
	for now := range time.Tick(1 * time.Millisecond) {
		fmt.Println("Tick!!")
		// time.Tickで取得した現在時間とループ開始直前の時間の差分でループを止めるか決める
		if now.Sub(begin) >= limit {
			break
		}
	}
}

終わりに

time.Tick()は内部的にはtime.Tickerを生成している。 time.Tickerを使うときは明示的にStop()を呼ばないとリークするらしいが、time.Tick()rangeで使っている場合はループのスコープが外れればGCされるのかな?

https://golang.org/src/time/tick.go?s=1752:1785#L44

func Tick(d Duration) <-chan Time {
	if d <= 0 {
		return nil
	}
	return NewTicker(d).C
}

参考

関連記事