Goのタイマーとティッカーの実用的なガイド
Olivia Novak
Dev Intern · Leapcell

まえがき
日々の開発において、いくつかのタスクの実行を遅らせたり、定期的に実行したりする必要がある状況に遭遇することがあります。この時点で、Goでタイマーを使用する必要があります。
Goには、time.Timer
(ワンショットタイマー) と time.Ticker
(定期的タイマー) の2種類のタイマーがあります。この記事では、両方の種類のタイマーを紹介します。
Timer: ワンショットタイマー
Timerは、将来の特定の時間に一度だけ操作を実行するために使用されるワンショットタイマーです。
基本的な使い方
Timerを作成する方法は2つあります。
NewTimer(d Duration) *Timer
: この関数は、time.Duration
型のパラメータd
(時間間隔) を受け入れます。これは、タイマーが期限切れになるまでに待機する時間を示します。NewTimer
は新しいTimerを返します。このTimerは、内部的にチャネルC
を保持します。タイマーが起動すると、現在の時刻がチャネルC
に送信されます。AfterFunc(d Duration, f func()) *Timer
: 指定された時間間隔d
とコールバック関数f
を受け入れます。この関数は新しいTimerを返し、タイマーが期限切れになると、チャネルC
を介してシグナルを送信する代わりに、直接f
を呼び出します。TimerのStop
メソッドを呼び出すと、タイマーを停止し、f
の実行をキャンセルできます。
次のコードは、NewTimer
と AfterFunc
を使用してタイマーを作成する方法と、その基本的な使い方を示しています。
package main import ( "fmt" "time" ) func main() { // NewTimerを使用してタイマーを作成します timer := time.NewTimer(time.Second) go func() { select { case <-timer.C: fmt.Println("タイマーが起動しました!") } }() // AfterFuncを使用して別のタイマーを作成します time.AfterFunc(time.Second, func() { fmt.Println("タイマー2が起動しました!") }) // メインゴルーチンは、タイマーの出力を確認するために2秒間待機します time.Sleep(time.Second * 2) }
上記のコードの出力は次のとおりです。
タイマーが起動しました! タイマー2が起動しました!
コードのステップごとの説明は次のとおりです。
NewTimer
を使用してタイマーを作成し、新しいゴルーチンでそのC
プロパティをリッスンして、タイマーが起動するのを待ちます。AfterFunc
を使用して別のタイマーを作成し、タイマーの有効期限イベントを処理するためのコールバック関数を指定します。- メインゴルーチンは、タイマーの起動情報を印刷するのに十分な時間待機します。
メソッドの詳細
Reset
Reset(d Duration) bool
: このメソッドは、Timerの有効期限をリセットするために使用され、基本的にそれを再アクティブ化します。これは、タイマーが期限切れになるまで待機する時間を示す time.Duration
型のパラメータ d
を受け入れます。
さらに、このメソッドは bool
値を返します。
- タイマーがアクティブな場合は、
true
を返します。 - タイマーが既に期限切れになっているか、停止されている場合は、
false
を返します (注:false
はリセットが失敗したことを意味するのではなく、タイマーの現在の状態を示すだけです)。
コード例を次に示します。
package main import ( "fmt" "time" ) func main() { timer := time.NewTimer(5 * time.Second) // 最初のReset: タイマーはアクティブなので、trueを返します b := timer.Reset(1 * time.Second) fmt.Println(b) // true second := time.Now().Second() select { case t := <-timer.C: fmt.Println(t.Second() - second) // 1s } // 2回目のReset: タイマーはすでに期限切れになっているので、falseを返します b = timer.Reset(2 * time.Second) fmt.Println(b) // false second = time.Now().Second() select { case t := <-timer.C: fmt.Println(t.Second() - second) // 2s } }
コードの出力は次のとおりです。
true 1 false 2
ステップごとの説明:
- 5秒後に期限切れになるように設定されたタイマーを作成します。
Reset
メソッドをすぐに呼び出して、1秒後に期限切れになるように設定します。タイマーはまだアクティブ (期限切れになっていない) なので、Reset
はtrue
を返します。select
ステートメントは、タイマーが期限切れになるのを待ってから、実際に経過した秒数 (約1秒) を出力します。- タイマーが再度リセットされ、今回は2秒後に期限切れになるように設定されます。タイマーはすでに期限切れになっているため、
Reset
はfalse
を返します。 select
ステートメントは、タイマーが期限切れになるのを再度待ってから、経過した秒数 (約2秒) を出力します。
Stop
Stop() bool
: このメソッドは、タイマーを停止するために使用されます。タイマーが正常に停止された場合、true
を返します。タイマーが既に期限切れになっているか、停止されている場合は、false
を返します。注: Stop
操作はチャネル C
を閉じません。
コード例を次に示します。
package main import ( "fmt" "time" ) func main() { timer := time.NewTimer(3 * time.Second) // タイマーが起動する前に停止します。したがって、trueを返します stop := timer.Stop() fmt.Println(stop) // true stop = timer.Stop() // タイマーを再度停止します。すでに停止しているため、falseを返します fmt.Println(stop) // false }
出力は次のとおりです。
true false
ステップごとの説明:
- 3秒後に起動するように設定されたタイマーを作成します。
Stop
メソッドをすぐに呼び出して、タイマーを停止します。タイマーはまだ起動していないため、Stop
はtrue
を返します。Stop
を再度呼び出して、同じタイマーを停止しようとします。すでに停止しているため、今回はStop
はfalse
を返します。
Ticker: 定期的タイマー
Tickerは、固定間隔でタスクを繰り返し実行するために使用される定期的タイマーです。各間隔で、現在の時刻をチャネルに送信します。
基本的な使い方
NewTicker
関数を使用して、新しいTickerオブジェクトを作成できます。この関数は、time.Duration
パラメータ d
(間隔) を受け入れます。
次に例を示します。
package main import ( "context" "fmt" "time" ) func main() { ticker := time.NewTicker(time.Second) defer ticker.Stop() timeout, cancelFunc := context.WithTimeout(context.Background(), time.Second*5) defer cancelFunc() go func() { for { select { case <-timeout.Done(): fmt.Println("タイムアウトしました") return case <-ticker.C: fmt.Println("タイマーが起動しました!") } } }() // メインゴルーチンは、タイマーの出力を確認するために7秒間待機します time.Sleep(time.Second * 7) }
コードの出力は次のとおりです。
タイマーが起動しました! タイマーが起動しました! タイマーが起動しました! タイマーが起動しました! タイマーが起動しました! タイムアウトしました
ステップごとの説明:
- 1秒ごとに起動するタイマーを作成します。関数の最後にタイマーがクリーンアップされるように、
defer ticker.Stop()
を使用します。 - 5秒後にタイムアウトするコンテキストを作成します。
cancelFunc
は、終了前にコンテキストをクリーンアップするために使用されます。 - 新しいゴルーチンで、
select
ステートメントは2つのチャネルをリッスンします。タイマーのチャネル (ticker.C
) とコンテキストの完了チャネル (timeout.Done()
)。タイマーが毎秒起動すると、メッセージが出力されます。コンテキストがタイムアウトすると (5秒後)、タイムアウトメッセージが出力されて戻り、ゴルーチンが終了します。 - メインゴルーチンは
time.Sleep(time.Second * 7)
を使用して7秒間待機し、タイマーの起動とタイムアウトイベントの両方を観測できるようにします。
select
で ticker.C
をリッスンするだけでなく、for range
ループを使用することもできます。
for range ticker.C {}
注: Stop
メソッドで Ticker を停止した場合でも、そのチャネル C
は閉じられません。つまり、for select
と for range
のどちらを使用して ticker.C
をリッスンする場合でも、コンテキストの使用など、ループを終了するための別のメカニズムが必要です。
メソッドの詳細
Reset
Reset(d Duration)
メソッドは、tickerを停止し、その期間を指定された期間にリセットするために使用されます。次のティックは、新しい期間が経過した後に発生します。これは、新しい間隔を表す time.Duration
型のパラメータ d
を受け入れます。このパラメータはゼロより大きくする必要があります。そうしないと、Reset
メソッドは内部的にパニックになります。
次に例を示します。
package main import ( "time" ) func main() { ticker := time.NewTicker(5 * time.Second) defer ticker.Stop() // Tickerをリセットします ticker.Reset(1 * time.Second) second := time.Now().Second() for t := range ticker.C { // 1s fmt.Printf("間隔: %d 秒", t.Second()-second) break } }
コードの出力は次のとおりです。
間隔: 1 秒
ステップごとの説明:
- 5秒ごとに起動する time.Ticker を作成します。
Reset
メソッドを使用して、間隔を5秒から1秒に変更します。- 単一のループで、間隔を出力します。期待される結果は 1 秒です。
Stop
Stop()
メソッドは、tickerを停止するために使用されます。Stop
を呼び出した後、チャネル C
にはティックが送信されなくなります。注: Stop
操作はチャネル C
を閉じません。
次に例を示します。
package main import ( "fmt" "time" ) func main() { ticker := time.NewTicker(time.Second) quit := make(chan struct{}) // quitチャネルを作成します go func() { for { select { case <-ticker.C: fmt.Println("Tickerが起動しました!") case <-quit: fmt.Println("ゴルーチンが停止しました!") return // quitシグナルを受信したらループを終了します } } }() time.Sleep(time.Second * 3) ticker.Stop() // Tickerを停止します close(quit) // quitシグナルを送信します fmt.Println("Tickerが停止しました!") }
出力は次のとおりです。
Tickerが起動しました! Tickerが起動しました! Tickerが起動しました! ゴルーチンが停止しました! Tickerが停止しました!
- 1秒ごとに起動する time.Ticker オブジェクトを作成します。同時に、実行中のゴルーチンに停止シグナルを送信するために使用される
chan struct{}{}
型の quit チャネルが導入されます。 - 新しいゴルーチンを開始します。このゴルーチンでは、for-select ループが 2 つのイベントをリッスンします。ticker の起動 (
case <-ticker.C
) と quit シグナル (case <-quit
)。ticker が起動するたびに、メッセージが出力されます。quit シグナルを受信すると、メッセージが出力されてループが終了します。 - メインゴルーチンでは、
time.Sleep(time.Second * 3)
は 3 秒の待機時間をシミュレートします。この間、ticker は数回起動します。 - メインゴルーチンは、
Stop
を呼び出して ticker を停止し、quit チャネルを閉じます。ゴルーチンは quit シグナルを受信し、メッセージを出力してループを終了します。
Stop
メソッドはチャネル C
を閉じないため、リソースをクリーンアップするには他の手段 (quit シグナルなど) を使用する必要があります。
TimerとTickerの主な違い
使い方:
- Timer は、単一の遅延後に実行されるタスクに使用されます。
- Ticker は、繰り返し実行する必要があるタスクに使用されます。
動作特性:
- Timer は、指定された遅延後に一度だけ起動し、単一の時刻値をそのチャネルに送信します。
- Ticker は、指定された間隔で定期的に起動し、繰り返される時刻値をそのチャネルに送信します。
制御性:
- Timer は、リセット (
Reset
メソッド) および停止 (Stop
メソッド) できます。Reset
は、Timer の起動時間を変更するために使用されます。 - Ticker も、リセット (
Reset
メソッド) および停止 (Stop
メソッド) できます。Reset
は、Ticker が起動する間隔を変更するために使用されます。
終了操作:
- Timer の
Stop
メソッドは、Timer が起動しないようにするために使用されます。 Timer がすでに起動している場合、Stop
はすでにチャネルに送信されている時刻値を削除しません。 - Ticker の
Stop
メソッドは、定期的な起動を停止するために使用されます。 停止すると、新しい値はチャネルに送信されなくなります。
注意点
- Timer と Ticker の両方について、
Stop
メソッドを呼び出しても、そのC
チャネルは閉じません。 このチャネルをリッスンしている他のゴルーチンがある場合、潜在的なメモリリークを回避するために、これらのゴルーチンを手動で終了する必要があります。 通常、このようなリソースのクリーンアップは、context
を使用するか、quit シグナル (チャネルで実装) を使用して処理できます。 - Ticker がタスクを完了したら、関連するリソースを解放し、メモリリークを防ぐために、
Stop
メソッドを呼び出す必要があります。 Ticker を時間内に停止しないと、継続的なリソース占有が発生する可能性があります。
まとめ
この記事では、Go のTimerとTickerについて深く掘り下げ、それらの作成方法、基本的な使い方、および関連するメソッドについて詳しく紹介しました。さらに、この記事では、これら2種類のタイマーの主な違いをまとめ、使用する際に留意すべき点を強調しています。
Go コードを作成するときは、アプリケーションシナリオに応じて適切なタイマーを選択する必要があります。同時に、ベストプラクティスに従うことが重要です。特に、タイマーの使用が終了した後にリソースを迅速に解放することは、潜在的なメモリリークを回避するために重要です。
Goプロジェクトのホスティングには、Leapcellをお選びください。
Leapcellは、Webホスティング、非同期タスク、およびRedis向けの次世代サーバーレスプラットフォームです。
多言語サポート
- Node.js、Python、Go、または Rust で開発します。
無制限のプロジェクトを無料でデプロイ
- 使用量に対してのみ支払い — リクエストも請求もありません。
圧倒的なコスト効率
- アイドル料金なしの従量課金制。
- 例:25ドルで平均応答時間60ミリ秒で694万リクエストをサポートします。
合理化された開発者エクスペリエンス
- 簡単なセットアップのための直感的なUI。
- 完全に自動化されたCI/CDパイプラインとGitOps統合。
- 実用的な洞察を得るためのリアルタイムのメトリックとロギング。
簡単なスケーラビリティと高性能
- 高い同時実行性を容易に処理するための自動スケーリング。
- 運用上のオーバーヘッドはゼロ — 構築に集中するだけです。
詳細については、ドキュメントをご覧ください!
Xでフォローしてください: @LeapcellHQ