[インデックス 11253] ファイルの概要
このコミットは、Go言語の公式ドキュメントに含まれるコードウォーク(codewalk)のサンプルプログラム doc/codewalk/urlpoll.go
に対する変更です。urlpoll.go
は、複数のURLを定期的にポーリングし、その状態を監視するシンプルな並行処理の例を示しています。このファイルは、Go言語のgoroutineとchannelを用いた並行処理のパターン、およびtime
パッケージの利用方法を学習するための教材として機能します。
コミット
commit 1de49311135e8e07e16f065f5c03fab9d7dae5e8
Author: Stefan Nilsson <snilsson@nada.kth.se>
Date: Thu Jan 19 14:45:59 2012 +1100
doc/codewalk: update urlpoll to use time.Duration.
R=adg
CC=golang-dev
https://golang.org/cl/5545061
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/1de49311135e8e07e16f065f5c03fab9d7dae5e8
元コミット内容
doc/codewalk: update urlpoll to use time.Duration.
変更の背景
このコミットの主な目的は、doc/codewalk/urlpoll.go
サンプルコードにおける時間間隔の表現方法を改善し、Go言語の標準的なプラクティスに合わせることです。以前のコードでは、時間間隔をナノ秒単位のint64
型で直接扱っていました。これは、数値としては正しいものの、その数値が「時間」を表すことをコードの読み手に明確に伝えることができませんでした。また、異なる時間単位(秒、ミリ秒など)を扱う際に、誤った単位での計算ミスを引き起こす可能性がありました。
Go言語のtime
パッケージには、期間(duration)を表現するための専用のtime.Duration
型が用意されています。この型を使用することで、時間に関する値をより型安全に、かつ意図を明確にして扱うことができます。この変更は、サンプルコードの品質と可読性を向上させ、Go言語のベストプラクティスを示すことを意図しています。
前提知識の解説
Go言語の time
パッケージと time.Duration
型
Go言語の標準ライブラリには、時間と日付を扱うためのtime
パッケージが含まれています。このパッケージには、特定の時点を表すtime.Time
型と、時間間隔(期間)を表すtime.Duration
型があります。
time.Duration
: これはint64
のエイリアス型であり、ナノ秒単位で期間を表します。しかし、単なるint64
とは異なり、time.Duration
型は時間に関する演算(加算、減算、乗算、除算)を型安全に行うためのメソッドを提供します。例えば、time.Second
、time.Minute
、time.Hour
といった定数と組み合わせて、60 * time.Second
のように記述することで、60秒という期間を直感的に表現できます。これにより、コードの可読性が向上し、単位変換のミスを防ぐことができます。
Go言語の型安全性
Go言語は静的型付け言語であり、型安全性を重視しています。型安全性とは、プログラムが意図しない型の値を扱ったり、型変換の誤りによって予期せぬ動作をしたりするのを防ぐ特性です。int64
で時間間隔を表現する場合、そのint64
が時間なのか、単なる数値なのかがコードからは判別しにくいです。しかし、time.Duration
型を使用することで、コンパイラがその値が時間間隔であることを認識し、時間に関する操作以外での誤用を防ぐことができます。これにより、バグの発生を抑制し、コードの信頼性を高めます。
Go言語の並行処理(GoroutineとChannel)
urlpoll.go
はGo言語の並行処理の基本的な概念であるgoroutineとchannelを使用しています。
- Goroutine: Go言語における軽量なスレッドのようなものです。
go
キーワードを関数呼び出しの前に置くことで、その関数を新しいgoroutineとして実行し、他の処理と並行して動作させることができます。 - Channel: goroutine間で安全にデータを送受信するための通信メカニズムです。channelを使用することで、共有メモリを直接操作することなく、goroutine間の同期と通信を行うことができます。これにより、データ競合(data race)のような並行処理に起因する複雑なバグを防ぐことができます。
urlpoll.go
では、pending
、complete
、status
といったchannelがgoroutine間のデータフローを管理しています。
技術的詳細
このコミットでは、urlpoll.go
内の時間間隔を扱う部分がint64
からtime.Duration
型に移行されました。具体的な変更点は以下の通りです。
-
定数の変更:
second = 1e9
というナノ秒を定義していた定数が削除されました。pollInterval
、statusInterval
、errTimeout
といった時間間隔を表す定数が、60 * second
のようなint64
の計算から、60 * time.Second
のようにtime.Duration
型の定数とtime
パッケージのSecond
定数を直接使用する形に変更されました。これにより、これらの定数が明確に「期間」を表すことが示されます。
-
StateMonitor
関数の引数型の変更:func StateMonitor(updateInterval int64)
がfunc StateMonitor(updateInterval time.Duration)
に変更されました。これにより、StateMonitor
関数が期待するupdateInterval
が時間間隔であることが明確になり、呼び出し側での誤ったint64
値の渡し方をコンパイラが検出できるようになります。
-
Resource
構造体のerrCount
フィールドの型変更:errCount int64
がerrCount int
に変更されました。これは、time.Duration
との乗算を考慮した変更です。Go言語では、time.Duration
とint
の乗算は定義されていますが、time.Duration
とint64
の乗算は直接定義されていません(time.Duration
はint64
のエイリアスですが、型システム上は異なる型として扱われます)。errCount
はエラー回数を表すため、負の値を取ることはなく、int
型で十分な範囲をカバーできます。
-
Resource.Sleep
メソッドの実装変更:time.Sleep(pollInterval + errTimeout*r.errCount)
がtime.Sleep(pollInterval + errTimeout*time.Duration(r.errCount))
に変更されました。- ここで、
r.errCount
(int
型)がtime.Duration
型に明示的に型変換されています。これにより、errTimeout
(time.Duration
)とtime.Duration(r.errCount)
(time.Duration
)の乗算が可能になり、結果としてtime.Duration
型の値がtime.Sleep
に渡されるようになります。この変更により、時間計算の型安全性が確保されます。
これらの変更は、コードの可読性を大幅に向上させ、時間に関するバグ(例えば、単位の誤解による計算ミス)のリスクを低減します。また、Go言語の標準的な慣習に従うことで、将来的なメンテナンスも容易になります。
コアとなるコードの変更箇所
--- a/doc/codewalk/urlpoll.go
+++ b/doc/codewalk/urlpoll.go
@@ -11,11 +11,10 @@ import (
)
const (
- numPollers = 2 // number of Poller goroutines to launch
- second = 1e9 // one second is 1e9 nanoseconds
- pollInterval = 60 * second // how often to poll each URL
- statusInterval = 10 * second // how often to log status to stdout
- errTimeout = 10 * second // back-off timeout on error
+ numPollers = 2 // number of Poller goroutines to launch
+ pollInterval = 60 * time.Second // how often to poll each URL
+ statusInterval = 10 * time.Second // how often to log status to stdout
+ errTimeout = 10 * time.Second // back-off timeout on error
)
var urls = []string{
@@ -33,7 +32,7 @@ type State struct {
// StateMonitor maintains a map that stores the state of the URLs being
// polled, and prints the current state every updateInterval nanoseconds.
// It returns a chan State to which resource state should be sent.\n-func StateMonitor(updateInterval int64) chan<- State {
+func StateMonitor(updateInterval time.Duration) chan<- State {
updates := make(chan State)\n urlStatus := make(map[string]string)\n ticker := time.NewTicker(updateInterval)\n@@ -61,7 +60,7 @@ func logState(s map[string]string) {
// Resource represents an HTTP URL to be polled by this program.\n type Resource struct {
url string
-\terrCount int64
+\terrCount int
}\n
// Poll executes an HTTP HEAD request for url
@@ -79,8 +78,8 @@ func (r *Resource) Poll() string {
// Sleep sleeps for an appropriate interval (dependant on error state)
// before sending the Resource to done.\n-func (r *Resource) Sleep(done chan *Resource) {
-\ttime.Sleep(pollInterval + errTimeout*r.errCount)\n+func (r *Resource) Sleep(done chan<- *Resource) {
+\ttime.Sleep(pollInterval + errTimeout*time.Duration(r.errCount))\n \tdone <- r
}\n
\n@@ -93,18 +92,18 @@ func Poller(in <-chan *Resource, out chan<- *Resource, status chan<- State) {
}\n
func main() {
-\t// create our input and output channels
+\t// Create our input and output channels.\n \tpending, complete := make(chan *Resource), make(chan *Resource)\n \n-\t// launch the StateMonitor
+\t// Launch the StateMonitor.\n \tstatus := StateMonitor(statusInterval)\n \n-\t// launch some Poller goroutines
+\t// Launch some Poller goroutines.\n \tfor i := 0; i < numPollers; i++ {\n \t\tgo Poller(pending, complete, status)\n \t}\n \n-\t// send some Resources to the pending queue
+\t// Send some Resources to the pending queue.\n \tgo func() {\n \t\tfor _, url := range urls {\n \t\t\tpending <- &Resource{url: url}\n```
## コアとなるコードの解説
### 定数定義の変更
```diff
- second = 1e9 // one second is 1e9 nanoseconds
- pollInterval = 60 * second // how often to poll each URL
- statusInterval = 10 * second // how often to log status to stdout
- errTimeout = 10 * second // back-off timeout on error
+ pollInterval = 60 * time.Second // how often to poll each URL
+ statusInterval = 10 * time.Second // how often to log status to stdout
+ errTimeout = 10 * time.Second // back-off timeout on error
second
定数(1秒をナノ秒で表すint64
)が削除されました。pollInterval
、statusInterval
、errTimeout
の各定数が、time.Second
というtime.Duration
型の定数を用いて直接定義されるようになりました。これにより、これらの値が時間間隔であることをコード上で明確に表現し、型安全性を高めています。例えば、60 * time.Second
は「60秒間」という期間を意味します。
StateMonitor
関数のシグネチャ変更
-func StateMonitor(updateInterval int64) chan<- State {
+func StateMonitor(updateInterval time.Duration) chan<- State {
StateMonitor
関数のupdateInterval
引数の型がint64
からtime.Duration
に変更されました。これにより、この関数が期待する引数が時間間隔であることが明確になり、呼び出し側で誤った数値が渡されることを防ぎます。
Resource
構造体のerrCount
フィールドの型変更
type Resource struct {
url string
- errCount int64
+ errCount int
}
Resource
構造体内のerrCount
フィールドの型がint64
からint
に変更されました。errCount
はエラーの回数を表すため、通常は非負の整数であり、int
型で十分な範囲をカバーできます。この変更は、後述するtime.Duration
との乗算をより自然に行うための準備でもあります。
Resource.Sleep
メソッドの実装変更
-func (r *Resource) Sleep(done chan *Resource) {
- time.Sleep(pollInterval + errTimeout*r.errCount)
+func (r *Resource) Sleep(done chan<- *Resource) {
+ time.Sleep(pollInterval + errTimeout*time.Duration(r.errCount))
done <- r
}
done
チャネルの型がchan *Resource
からchan<- *Resource
に変更されました。これは、このチャネルがSleep
メソッド内で送信専用として使用されることを明示するもので、機能的な変更ではありませんが、コードの意図を明確にします。time.Sleep
に渡す引数の計算式が変更されました。- 変更前:
pollInterval + errTimeout*r.errCount
- 変更後:
pollInterval + errTimeout*time.Duration(r.errCount)
- 変更前:
r.errCount
(int
型)がtime.Duration(r.errCount)
として明示的にtime.Duration
型に変換されています。これにより、errTimeout
(time.Duration
)とtime.Duration(r.errCount)
(time.Duration
)の乗算が可能になり、結果としてtime.Duration
型の値がtime.Sleep
関数に渡されます。これは、エラー回数に応じてバックオフする時間を計算する際に、型安全な時間演算を適用するための重要な変更です。
main
関数のコメント変更
func main() {
- // create our input and output channels
+ // Create our input and output channels.
pending, complete := make(chan *Resource), make(chan *Resource)
- // launch the StateMonitor
+ // Launch the StateMonitor.
status := StateMonitor(statusInterval)
- // launch some Poller goroutines
+ // Launch some Poller goroutines.
for i := 0; i < numPollers; i++ {
go Poller(pending, complete, status)
}
- // send some Resources to the pending queue
+ // Send some Resources to the pending queue.
go func() {
for _, url := range urls {
pending <- &Resource{url: url}
main
関数内のコメントが、小文字始まりから大文字始まりに変更され、末尾にピリオドが追加されました。これは、Go言語の公式ドキュメントやサンプルコードにおけるコメントスタイルの統一を目的とした、軽微な整形変更です。
関連リンク
- Go言語
time
パッケージの公式ドキュメント: https://pkg.go.dev/time - Go言語のコードウォーク(Codewalks): https://go.dev/doc/codewalk/ (このコミットが関連する
urlpoll.go
の元の場所)
参考にした情報源リンク
- Go言語の公式ドキュメント (
time
パッケージ) - Go言語の型システムに関する一般的な知識
- Go言語の並行処理に関する一般的な知識
- GitHubのコミット履歴と差分表示
- Go言語のコードレビュー慣習 (コメントスタイルなど)
- Go言語の
time.Duration
に関するブログ記事やチュートリアル (例: "Go time.Duration" で検索して得られる情報)- 例: https://yourbasic.org/golang/time-duration/ (一般的なGoの
time.Duration
の解説記事) - 例: https://www.digitalocean.com/community/tutorials/how-to-work-with-time-in-go (Goにおける時間操作のチュートリアル)
- 例: https://yourbasic.org/golang/time-duration/ (一般的なGoの