今川館

都内勤務の地味OLです

Goでスリープしようとしてハマった

Pythonでスリープするときは

import time

time.sleep(1)  # 1秒待つ
time.sleep(0.5)  # 0.5秒待つ

こうすれば良いが、同じ感覚でGoでスリープしようとしたらうまくいかなかった。

import "time"

time.Sleep(1) // 1秒待つ(?)

どうなるかというと、やけに早く戻ってきてしまうのである。

ちなみに、こっちをやるとコンパイルエラーになってしまう。

time.Sleep(0.5)
// constant 0.5 truncated to integer

Goのtime.Sleepの引数はDurationという型

https://golang.org/pkg/time/#Sleep

golangのドキュメントを見ると、time.Sleepの引数にはtime.Durationという型を受け付ける。

そして、time.Durationはint64の別名型らしい。

type Duration int64

A Duration represents the elapsed time between two instants as an int64 nanosecond count.

しかもナノ秒が基礎なので、time.Sleep(1)とやってしまうとたった1ナノ秒しか待たないことになる。
どうりで思ったより早く戻ってくるわけだ。

正式なN秒スリープの仕方

N秒スリープするのに正式も自己流も無いと思うが、以下の書き方が安定的である。

time.Sleep(1 * time.Second) // 1秒待つ

先ほど書いた通り、0.5秒待つときにこれはできない。

time.Sleep(0.5 * time.Second)  // これはエラー(floatと掛け算できない) constant 0.5 truncated to integer

こうすれば動く。

time.Sleep(500 * time.Millisecond) // 0.5秒待つときはMillisecondと乗算する

ちなみにこれも動く。

var s time.Duration
s = 5
time.Sleep(100 * s * time.Millisecond)

しかし、こっちは動かない。int64が掛け算の式に混入するのがダメらしい。

var s int64 // この他、float64もintも試したがダメだった
s = 5
time.Sleep(100 * s * time.Millisecond)
// invalid operation: 100 * s * time.Millisecond (mismatched types int64 and time.Duration)

どうしてもintと掛け算したい場合はこのように一度整数からDurationを作ってから掛け算する。

var t int
t = 5
time.Sleep(time.Duration(t*100) * time.Millisecond)
fmt.Println("Hello, world.")

なぜ、こんなことを調べ始めたかというと、「ランダムに数百ミリ秒待つ」という処理を書こうとしてハマったからである。

(続く)