Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

[インデックス 12647] ファイルの概要

このコミットは、Go言語の標準ライブラリである net/http パッケージ内の triv.go ファイルに対する変更です。triv.go は、net/http パッケージの基本的な使用例や、expvar パッケージとの連携を示すための、いわゆる「自明な(trivial)」なHTTPサーバーの例を含んでいます。具体的には、シンプルな「Hello World」サーバー、カウンタースサーバー、ロガー、ファイルサーバーなどの機能が実装されています。このファイルは、net/http パッケージの機能をデモンストレーションするためのサンプルコード集としての役割を担っています。

コミット

このコミットは、net/http パッケージの triv.go ファイルに対して、いくつかの「現代化(modernizations)」を適用するものです。主な変更点は、カウンタースサーバーの並行処理安全性の確保(スレッドセーフ化)と、HTTPハンドラの登録方法の簡素化です。これにより、サンプルコードがより堅牢で、現代のGoの慣用的な書き方に沿ったものになっています。

GitHub上でのコミットページへのリンク

https://github.com/golang/go/commit/a4e6197b91e2bc16616245a3a704b89ab99db949

元コミット内容

net/http: couple more triv.go modernizations

R=golang-dev, rsc
CC=golang-dev
https://golang.org/cl/5834049

変更の背景

この変更の背景には、主に以下の2つの目的があります。

  1. カウンタースサーバーの並行処理安全性(スレッドセーフティ)の確保: 以前の Counter 実装では、複数のHTTPリクエストが同時にカウンタの値を更新しようとした場合、競合状態(race condition)が発生し、正確なカウントが保証されない可能性がありました。これは、並行処理がGo言語の重要な特徴であるにもかかわらず、サンプルコードがそのベストプラクティスに従っていなかったことを意味します。このコミットは、sync.Mutex を導入することで、カウンタの増減操作を排他的に保護し、並行アクセス時のデータ破損を防ぐことを目的としています。
  2. HTTPハンドラ登録の慣用的な書き方への移行: http.Handlehttp.HandlerFunc の組み合わせは有効ですが、Go 1.0以降では http.HandleFunc というより簡潔な関数が提供されています。これは、func(ResponseWriter, *Request) 型の関数を直接ハンドラとして登録できる糖衣構文(syntactic sugar)であり、コードの可読性と記述性を向上させます。この変更は、サンプルコードをGoの最新の慣用的な書き方に合わせることで、新規開発者にとってより良い学習リソースとなることを意図しています。
  3. webroot の初期値の改善: webroot のデフォルト値がハードコードされたパス (/home/rsc) から、ユーザーのホームディレクトリ (os.Getenv("HOME")) に変更されました。これにより、サンプルコードがより汎用的に、異なる環境で動作するようになります。

これらの変更は、triv.go が単なるサンプルコードではなく、Goのベストプラクティスと現代的なコーディングスタイルを示す模範となるようにするための継続的な改善の一環です。

前提知識の解説

Go言語の並行処理とsync.Mutex

Go言語は、ゴルーチン(goroutine)とチャネル(channel)を用いた並行処理を強力にサポートしています。しかし、複数のゴルーチンが共有データに同時にアクセスする場合、競合状態が発生し、予期せぬ結果やデータ破損を引き起こす可能性があります。これを防ぐために、Goの標準ライブラリには sync パッケージが提供されており、その中に sync.Mutex(ミューテックス)という排他制御の仕組みがあります。

  • ミューテックス(Mutex): Mutual Exclusion(相互排他)の略で、複数のゴルーチンが同時に共有リソースにアクセスするのを防ぐための同期プリミティブです。ミューテックスは Lock() メソッドと Unlock() メソッドを持ちます。
    • Lock(): ミューテックスをロックします。既にロックされている場合、現在のゴルーチンはロックが解放されるまでブロックされます。
    • Unlock(): ミューテックスをアンロックします。これにより、他のゴルーチンがロックを取得できるようになります。
  • defer ステートメント: Go言語の defer ステートメントは、関数がリターンする直前に実行される関数呼び出しをスケジュールします。これは、リソースの解放(例: ファイルのクローズ、ミューテックスのアンロック)を確実に行うために非常に便利です。defer ctr.mu.Unlock() のように記述することで、関数のどのパスを通っても確実にミューテックスがアンロックされることが保証されます。

net/http パッケージのハンドラ登録

Goの net/http パッケージは、HTTPサーバーを構築するための基本的な機能を提供します。HTTPリクエストを処理するためには、特定のパス(URL)に対してハンドラを登録する必要があります。

  • http.Handler インターフェース: net/http パッケージの中心的なインターフェースの一つで、ServeHTTP(ResponseWriter, *Request) メソッドを一つだけ持ちます。このインターフェースを実装する任意の型は、HTTPリクエストを処理するハンドラとして機能できます。
  • http.HandlerFunc: これは func(ResponseWriter, *Request) 型の関数を http.Handler インターフェースに適合させるためのアダプタ(型)です。http.HandlerFunc(myFunction) のようにキャストすることで、通常の関数を http.Handler として扱うことができます。
  • http.Handle(pattern string, handler Handler): 指定されたパターン(URLパス)に対して、http.Handler インターフェースを実装するハンドラを登録します。
  • http.HandleFunc(pattern string, handler func(ResponseWriter, *Request)): これは http.Handle の糖衣構文であり、func(ResponseWriter, *Request) 型の関数を直接ハンドラとして登録できます。内部的には http.HandlerFunc を使用して関数を http.Handler に変換し、http.Handle を呼び出します。より簡潔に記述できるため、一般的にこちらが推奨されます。

expvar パッケージ

expvar パッケージは、Goプログラムの内部状態をHTTP経由で公開するためのシンプルな標準パッケージです。プログラムの実行中にメトリクスやデバッグ情報を簡単に監視できるように設計されています。

  • expvar.Var インターフェース: String() string メソッドを持つインターフェースです。このインターフェースを実装する任意の型は、expvar パッケージによって公開される変数として機能できます。
  • expvar.Publish(name string, v Var): 指定された名前で expvar.Var インターフェースを実装する変数を公開します。公開された変数は、デフォルトで /debug/vars というHTTPパスでJSON形式でアクセスできるようになります。

os.Getenv("HOME")

os パッケージは、オペレーティングシステムとの相互作用のための機能を提供します。os.Getenv(key string) 関数は、指定された環境変数 key の値を文字列として返します。"HOME" は、Unix系システムにおけるユーザーのホームディレクトリのパスを示す環境変数です。これにより、プログラムが実行される環境に依存しない、よりポータブルなパス指定が可能になります。

技術的詳細

Counter のスレッドセーフ化

変更前は、Counter 構造体は単に n int というカウンタ変数を持っていました。ServeHTTP メソッド内で ctr.n++ のように直接 n をインクリメントしていましたが、これは複数のHTTPリクエストが同時にこのハンドラに到達した場合、競合状態を引き起こす可能性がありました。例えば、2つのリクエストがほぼ同時に n の値を読み込み、それぞれがインクリメントして書き戻す場合、期待される n+2 ではなく n+1 になってしまう可能性があります(Lost Update問題)。

このコミットでは、この問題を解決するために sync.Mutex が導入されました。

type Counter struct {
	mu sync.Mutex // protects n
	n  int
}

mu sync.Mutex フィールドが追加され、n 変数を保護する役割を担うことがコメントで明示されています。

そして、CounterString() メソッドと ServeHTTP() メソッドの冒頭で ctr.mu.Lock() が呼び出され、defer ctr.mu.Unlock() によって関数の終了時に確実にロックが解放されるように変更されました。

func (ctr *Counter) String() string {
	ctr.mu.Lock()
	defer ctr.mu.Unlock()
	return fmt.Sprintf("%d", ctr.n)
}

func (ctr *Counter) ServeHTTP(w http.ResponseWriter, req *http.Request) {
	ctr.mu.Lock()
	defer ctr.mu.Unlock()
	switch req.Method {
	case "GET":
		ctr.n++
		fmt.Fprintf(w, "counter = %d\n", ctr.n)
	case "POST":
		// ... (POST処理)
	}
}

これにより、Countern フィールドへのアクセス(読み取りおよび書き込み)は、常にミューテックスによって保護されるようになり、複数のゴルーチンからの同時アクセスによる競合状態が排除され、カウンタがスレッドセーフになりました。

HTTPハンドラ登録の簡素化

以前のコードでは、http.HandlerFunc を明示的に使用して関数を http.Handler インターフェースに適合させてから http.Handle に渡していました。

http.Handle("/flags", http.HandlerFunc(FlagServer))
http.Handle("/args", http.HandlerFunc(ArgServer))
http.Handle("/go/hello", http.HandlerFunc(HelloServer))
http.Handle("/date", http.HandlerFunc(DateServer))

このコミットでは、これらの行が http.HandleFunc を使用するように変更されました。

http.HandleFunc("/flags", FlagServer)
http.HandleFunc("/args", ArgServer)
http.HandleFunc("/go/hello", HelloServer)
http.HandleFunc("/date", DateServer)

http.HandleFunc は、func(ResponseWriter, *Request) 型の関数を直接引数として受け取ることができるため、http.HandlerFunc による型変換が不要になり、コードがより簡潔で読みやすくなりました。これは機能的な変更ではなく、Goの慣用的なコーディングスタイルへの移行です。

webroot の初期値の変更

webroot という flag.String で定義された変数のデフォルト値が変更されました。

変更前:

var webroot = flag.String("root", "/home/rsc", "web root directory")

変更後:

var webroot = flag.String("root", os.Getenv("HOME"), "web root directory")

これにより、webroot のデフォルト値が、特定のユーザーのホームディレクトリ(/home/rsc)から、プログラムが実行される環境のユーザーのホームディレクトリ(os.Getenv("HOME"))に動的に設定されるようになりました。これは、異なる環境でこのサンプルコードを実行する際の利便性を向上させます。

コアとなるコードの変更箇所

src/pkg/net/http/triv.go ファイルにおける変更箇所は以下の通りです。

--- a/src/pkg/net/http/triv.go
+++ b/src/pkg/net/http/triv.go
@@ -17,6 +17,7 @@ import (
 	"os"
 	"os/exec"
 	"strconv"
+	"sync" // 追加: syncパッケージのインポート
 )
 
 // hello world, the web server
@@ -29,14 +30,21 @@ func HelloServer(w http.ResponseWriter, req *http.Request) {
 
 // Simple counter server. POSTing to it will set the value.
 type Counter struct {
-	n int
+	mu sync.Mutex // protects n (追加: ミューテックスフィールド)
+	n  int
 }
 
 // This makes Counter satisfy the expvar.Var interface, so we can export
 // it directly.
-func (ctr *Counter) String() string { return fmt.Sprintf("%d", ctr.n) }
+func (ctr *Counter) String() string {
+	ctr.mu.Lock()   // 追加: ロック
+	defer ctr.mu.Unlock() // 追加: アンロックの遅延実行
+	return fmt.Sprintf("%d", ctr.n)
+}
 
 func (ctr *Counter) ServeHTTP(w http.ResponseWriter, req *http.Request) {
+	ctr.mu.Lock()   // 追加: ロック
+	defer ctr.mu.Unlock() // 追加: アンロックの遅延実行
 	switch req.Method {
 	case "GET":
 		ctr.n++
@@ -110,23 +118,22 @@ func Logger(w http.ResponseWriter, req *http.Request) {
 	http.Error(w, "oops", 404)
 }
 
-var webroot = flag.String("root", "/home/rsc", "web root directory")
+var webroot = flag.String("root", os.Getenv("HOME"), "web root directory") // 変更: デフォルト値の変更
 
 func main() {
 	flag.Parse()
 
 	// The counter is published as a variable directly.
 	ctr := new(Counter)
-	http.Handle("/counter", ctr) // 削除: この行は下に移動
 	expvar.Publish("counter", ctr)
-
+	http.Handle("/counter", ctr) // 追加: 上の行から移動
 	http.Handle("/", http.HandlerFunc(Logger))
 	http.Handle("/go/", http.StripPrefix("/go/", http.FileServer(http.Dir(*webroot))))
-	http.Handle("/flags", http.HandlerFunc(FlagServer)) // 変更: HandleFuncへ
-	http.Handle("/args", http.HandlerFunc(ArgServer))   // 変更: HandleFuncへ
-	http.Handle("/go/hello", http.HandlerFunc(HelloServer)) // 変更: HandleFuncへ
 	http.Handle("/chan", ChanCreate())
-	http.Handle("/date", http.HandlerFunc(DateServer))  // 変更: HandleFuncへ
+	http.HandleFunc("/flags", FlagServer)       // 追加: HandleFuncで登録
+	http.HandleFunc("/args", ArgServer)         // 追加: HandleFuncで登録
+	http.HandleFunc("/go/hello", HelloServer)   // 追加: HandleFuncで登録
+	http.HandleFunc("/date", DateServer)        // 追加: HandleFuncで登録
 	err := http.ListenAndServe(":12345", nil)
 	if err != nil {
 		log.Panicln("ListenAndServe:", err)

コアとなるコードの解説

  1. import "sync" の追加:

    • Counter 構造体の n フィールドへの並行アクセスを保護するために、sync パッケージがインポートされました。これにより、sync.Mutex を使用できるようになります。
  2. Counter 構造体への mu sync.Mutex フィールドの追加:

    • type Counter struct { mu sync.Mutex; n int }
    • mu はミューテックスであり、n フィールドへのアクセスを排他的に制御します。これにより、複数のゴルーチンが同時に n を読み書きしようとした際の競合状態を防ぎます。
  3. Counter.String() メソッドと Counter.ServeHTTP() メソッドでのミューテックスの使用:

    • ctr.mu.Lock()defer ctr.mu.Unlock() が各メソッドの冒頭に追加されました。
    • Lock() は、共有リソース(この場合は ctr.n)へのアクセスを開始する前にミューテックスをロックします。
    • defer ctr.mu.Unlock() は、メソッドが終了する際に(正常終了、エラー終了に関わらず)ミューテックスをアンロックすることを保証します。これにより、n の読み取りや更新操作がアトミック(不可分)になり、並行処理安全性が確保されます。
  4. webroot 変数のデフォルト値の変更:

    • var webroot = flag.String("root", os.Getenv("HOME"), "web root directory")
    • 以前はハードコードされたパス(/home/rsc)でしたが、os.Getenv("HOME") を使用することで、実行環境のユーザーのホームディレクトリをデフォルト値として設定するようになりました。これにより、サンプルコードの移植性が向上します。
  5. http.Handle から http.HandleFunc への移行:

    • main 関数内で、FlagServer, ArgServer, HelloServer, DateServer といったハンドラ関数の登録に http.Handle("/path", http.HandlerFunc(Handler)) の形式ではなく、より簡潔な http.HandleFunc("/path", Handler) の形式が使用されるようになりました。
    • これは機能的な変更ではなく、Goの慣用的な書き方への「現代化」であり、コードの可読性と記述性を向上させます。

関連リンク

参考にした情報源リンク

  • Go言語公式ドキュメント (上記「関連リンク」に記載の各パッケージのドキュメント)
  • Go言語の並行処理に関する一般的な情報源 (例: Go Concurrency Patterns, Effective Goなど)
  • Go言語のミューテックスに関する解説記事 (例: A Tour of Go - Mutexes)
  • Go言語のHTTPハンドラに関する解説記事 (例: Writing HTTP servers in Go)```markdown

[インデックス 12647] ファイルの概要

このコミットは、Go言語の標準ライブラリである net/http パッケージ内の triv.go ファイルに対する変更です。triv.go は、net/http パッケージの基本的な使用例や、expvar パッケージとの連携を示すための、いわゆる「自明な(trivial)」なHTTPサーバーの例を含んでいます。具体的には、シンプルな「Hello World」サーバー、カウンタースサーバー、ロガー、ファイルサーバーなどの機能が実装されています。このファイルは、net/http パッケージの機能をデモンストレーションするためのサンプルコード集としての役割を担っています。

コミット

このコミットは、net/http パッケージの triv.go ファイルに対して、いくつかの「現代化(modernizations)」を適用するものです。主な変更点は、カウンタースサーバーの並行処理安全性の確保(スレッドセーフ化)と、HTTPハンドラの登録方法の簡素化です。これにより、サンプルコードがより堅牢で、現代のGoの慣用的な書き方に沿ったものになっています。

GitHub上でのコミットページへのリンク

https://github.com/golang/go/commit/a4e6197b91e2bc16616245a3a704b89ab99db949

元コミット内容

net/http: couple more triv.go modernizations

R=golang-dev, rsc
CC=golang-dev
https://golang.org/cl/5834049

変更の背景

この変更の背景には、主に以下の2つの目的があります。

  1. カウンタースサーバーの並行処理安全性(スレッドセーフティ)の確保: 以前の Counter 実装では、複数のHTTPリクエストが同時にカウンタの値を更新しようとした場合、競合状態(race condition)が発生し、正確なカウントが保証されない可能性がありました。これは、並行処理がGo言語の重要な特徴であるにもかかわらず、サンプルコードがそのベストプラクティスに従っていなかったことを意味します。このコミットは、sync.Mutex を導入することで、カウンタの増減操作を排他的に保護し、並行アクセス時のデータ破損を防ぐことを目的としています。
  2. HTTPハンドラ登録の慣用的な書き方への移行: http.Handlehttp.HandlerFunc の組み合わせは有効ですが、Go 1.0以降では http.HandleFunc というより簡潔な関数が提供されています。これは、func(ResponseWriter, *Request) 型の関数を直接ハンドラとして登録できる糖衣構文(syntactic sugar)であり、コードの可読性と記述性を向上させます。この変更は、サンプルコードをGoの最新の慣用的な書き方に合わせることで、新規開発者にとってより良い学習リソースとなることを意図しています。
  3. webroot の初期値の改善: webroot のデフォルト値がハードコードされたパス (/home/rsc) から、ユーザーのホームディレクトリ (os.Getenv("HOME")) に変更されました。これにより、サンプルコードがより汎用的に、異なる環境で動作するようになります。

これらの変更は、triv.go が単なるサンプルコードではなく、Goのベストプラクティスと現代的なコーディングスタイルを示す模範となるようにするための継続的な改善の一環です。

前提知識の解説

Go言語の並行処理とsync.Mutex

Go言語は、ゴルーチン(goroutine)とチャネル(channel)を用いた並行処理を強力にサポートしています。しかし、複数のゴルーチンが共有データに同時にアクセスする場合、競合状態が発生し、予期せぬ結果やデータ破損を引き起こす可能性があります。これを防ぐために、Goの標準ライブラリには sync パッケージが提供されており、その中に sync.Mutex(ミューテックス)という排他制御の仕組みがあります。

  • ミューテックス(Mutex): Mutual Exclusion(相互排他)の略で、複数のゴルーチンが同時に共有リソースにアクセスするのを防ぐための同期プリミティブです。ミューテックスは Lock() メソッドと Unlock() メソッドを持ちます。
    • Lock(): ミューテックスをロックします。既にロックされている場合、現在のゴルーチンはロックが解放されるまでブロックされます。
    • Unlock(): ミューテックスをアンロックします。これにより、他のゴルーチンがロックを取得できるようになります。
  • defer ステートメント: Go言語の defer ステートメントは、関数がリターンする直前に実行される関数呼び出しをスケジュールします。これは、リソースの解放(例: ファイルのクローズ、ミューテックスのアンロック)を確実に行うために非常に便利です。defer ctr.mu.Unlock() のように記述することで、関数のどのパスを通っても確実にミューテックスがアンロックされることが保証されます。

net/http パッケージのハンドラ登録

Goの net/http パッケージは、HTTPサーバーを構築するための基本的な機能を提供します。HTTPリクエストを処理するためには、特定のパス(URL)に対してハンドラを登録する必要があります。

  • http.Handler インターフェース: net/http パッケージの中心的なインターフェースの一つで、ServeHTTP(ResponseWriter, *Request) メソッドを一つだけ持ちます。このインターフェースを実装する任意の型は、HTTPリクエストを処理するハンドラとして機能できます。
  • http.HandlerFunc: これは func(ResponseWriter, *Request) 型の関数を http.Handler インターフェースに適合させるためのアダプタ(型)です。http.HandlerFunc(myFunction) のようにキャストすることで、通常の関数を http.Handler として扱うことができます。
  • http.Handle(pattern string, handler Handler): 指定されたパターン(URLパス)に対して、http.Handler インターフェースを実装するハンドラを登録します。
  • http.HandleFunc(pattern string, handler func(ResponseWriter, *Request)): これは http.Handle の糖衣構文であり、func(ResponseWriter, *Request) 型の関数を直接ハンドラとして登録できます。内部的には http.HandlerFunc を使用して関数を http.Handler に変換し、http.Handle を呼び出します。より簡潔に記述できるため、一般的にこちらが推奨されます。

expvar パッケージ

expvar パッケージは、Goプログラムの内部状態をHTTP経由で公開するためのシンプルな標準パッケージです。プログラムの実行中にメトリクスやデバッグ情報を簡単に監視できるように設計されています。

  • expvar.Var インターフェース: String() string メソッドを持つインターフェースです。このインターフェースを実装する任意の型は、expvar パッケージによって公開される変数として機能できます。
  • expvar.Publish(name string, v Var): 指定された名前で expvar.Var インターフェースを実装する変数を公開します。公開された変数は、デフォルトで /debug/vars というHTTPパスでJSON形式でアクセスできるようになります。

os.Getenv("HOME")

os パッケージは、オペレーティングシステムとの相互作用のための機能を提供します。os.Getenv(key string) 関数は、指定された環境変数 key の値を文字列として返します。"HOME" は、Unix系システムにおけるユーザーのホームディレクトリのパスを示す環境変数です。これにより、プログラムが実行される環境に依存しない、よりポータブルなパス指定が可能になります。

技術的詳細

Counter のスレッドセーフ化

変更前は、Counter 構造体は単に n int というカウンタ変数を持っていました。ServeHTTP メソッド内で ctr.n++ のように直接 n をインクリメントしていましたが、これは複数のHTTPリクエストが同時にこのハンドラに到達した場合、競合状態を引き起こす可能性がありました。例えば、2つのリクエストがほぼ同時に n の値を読み込み、それぞれがインクリメントして書き戻す場合、期待される n+2 ではなく n+1 になってしまう可能性があります(Lost Update問題)。

このコミットでは、この問題を解決するために sync.Mutex が導入されました。

type Counter struct {
	mu sync.Mutex // protects n
	n  int
}

mu sync.Mutex フィールドが追加され、n 変数を保護する役割を担うことがコメントで明示されています。

そして、CounterString() メソッドと ServeHTTP() メソッドの冒頭で ctr.mu.Lock() が呼び出され、defer ctr.mu.Unlock() によって関数の終了時に確実にロックが解放されるように変更されました。

func (ctr *Counter) String() string {
	ctr.mu.Lock()
	defer ctr.mu.Unlock()
	return fmt.Sprintf("%d", ctr.n)
}

func (ctr *Counter) ServeHTTP(w http.ResponseWriter, req *http.Request) {
	ctr.mu.Lock()
	defer ctr.mu.Unlock()
	switch req.Method {
	case "GET":
		ctr.n++
		fmt.Fprintf(w, "counter = %d\n", ctr.n)
	case "POST":
		// ... (POST処理)
	}
}

これにより、Countern フィールドへのアクセス(読み取りおよび書き込み)は、常にミューテックスによって保護されるようになり、複数のゴルーチンからの同時アクセスによる競合状態が排除され、カウンタがスレッドセーフになりました。

HTTPハンドラ登録の簡素化

以前のコードでは、http.HandlerFunc を明示的に使用して関数を http.Handler インターフェースに適合させてから http.Handle に渡していました。

http.Handle("/flags", http.HandlerFunc(FlagServer))
http.Handle("/args", http.HandlerFunc(ArgServer))
http.Handle("/go/hello", http.HandlerFunc(HelloServer))
http.Handle("/date", http.HandlerFunc(DateServer))

このコミットでは、これらの行が http.HandleFunc を使用するように変更されました。

http.HandleFunc("/flags", FlagServer)
http.HandleFunc("/args", ArgServer)
http.HandleFunc("/go/hello", HelloServer)
http.HandleFunc("/date", DateServer)

http.HandleFunc は、func(ResponseWriter, *Request) 型の関数を直接引数として受け取ることができるため、http.HandlerFunc による型変換が不要になり、コードがより簡潔で読みやすくなりました。これは機能的な変更ではなく、Goの慣用的なコーディングスタイルへの移行です。

webroot の初期値の変更

webroot という flag.String で定義された変数のデフォルト値が変更されました。

変更前:

var webroot = flag.String("root", "/home/rsc", "web root directory")

変更後:

var webroot = flag.String("root", os.Getenv("HOME"), "web root directory")

これにより、webroot のデフォルト値が、特定のユーザーのホームディレクトリ(/home/rsc)から、プログラムが実行される環境のユーザーのホームディレクトリ(os.Getenv("HOME"))に動的に設定されるようになりました。これは、異なる環境でこのサンプルコードを実行する際の利便性を向上させます。

コアとなるコードの変更箇所

src/pkg/net/http/triv.go ファイルにおける変更箇所は以下の通りです。

--- a/src/pkg/net/http/triv.go
+++ b/src/pkg/net/http/triv.go
@@ -17,6 +17,7 @@ import (
 	"os"
 	"os/exec"
 	"strconv"
+	"sync" // 追加: syncパッケージのインポート
 )
 
 // hello world, the web server
@@ -29,14 +30,21 @@ func HelloServer(w http.ResponseWriter, req *http.Request) {
 
 // Simple counter server. POSTing to it will set the value.
 type Counter struct {
-	n int
+	mu sync.Mutex // protects n (追加: ミューテックスフィールド)
+	n  int
 }
 
 // This makes Counter satisfy the expvar.Var interface, so we can export
 // it directly.
-func (ctr *Counter) String() string { return fmt.Sprintf("%d", ctr.n) }
+func (ctr *Counter) String() string {
+	ctr.mu.Lock()   // 追加: ロック
+	defer ctr.mu.Unlock() // 追加: アンロックの遅延実行
+	return fmt.Sprintf("%d", ctr.n)
+}
 
 func (ctr *Counter) ServeHTTP(w http.ResponseWriter, req *http.Request) {
+	ctr.mu.Lock()   // 追加: ロック
+	defer ctr.mu.Unlock() // 追加: アンロックの遅延実行
 	switch req.Method {
 	case "GET":
 		ctr.n++
@@ -110,23 +118,22 @@ func Logger(w http.ResponseWriter, req *http.Request) {
 	http.Error(w, "oops", 404)
 }
 
-var webroot = flag.String("root", "/home/rsc", "web root directory")
+var webroot = flag.String("root", os.Getenv("HOME"), "web root directory") // 変更: デフォルト値の変更
 
 func main() {
 	flag.Parse()
 
 	// The counter is published as a variable directly.
 	ctr := new(Counter)
-	http.Handle("/counter", ctr) // 削除: この行は下に移動
 	expvar.Publish("counter", ctr)
-
+	http.Handle("/counter", ctr) // 追加: 上の行から移動
 	http.Handle("/", http.HandlerFunc(Logger))
 	http.Handle("/go/", http.StripPrefix("/go/", http.FileServer(http.Dir(*webroot))))
-	http.Handle("/flags", http.HandlerFunc(FlagServer)) // 変更: HandleFuncへ
-	http.Handle("/args", http.HandlerFunc(ArgServer))   // 変更: HandleFuncへ
-	http.Handle("/go/hello", http.HandlerFunc(HelloServer)) // 変更: HandleFuncへ
 	http.Handle("/chan", ChanCreate())
-	http.Handle("/date", http.HandlerFunc(DateServer))  // 変更: HandleFuncへ
+	http.HandleFunc("/flags", FlagServer)       // 追加: HandleFuncで登録
+	http.HandleFunc("/args", ArgServer)         // 追加: HandleFuncで登録
+	http.HandleFunc("/go/hello", HelloServer)   // 追加: HandleFuncで登録
+	http.HandleFunc("/date", DateServer)        // 追加: HandleFuncで登録
 	err := http.ListenAndServe(":12345", nil)
 	if err != nil {
 		log.Panicln("ListenAndServe:", err)

コアとなるコードの解説

  1. import "sync" の追加:

    • Counter 構造体の n フィールドへの並行アクセスを保護するために、sync パッケージがインポートされました。これにより、sync.Mutex を使用できるようになります。
  2. Counter 構造体への mu sync.Mutex フィールドの追加:

    • type Counter struct { mu sync.Mutex; n int }
    • mu はミューテックスであり、n フィールドへのアクセスを排他的に制御します。これにより、複数のゴルーチンが同時に n を読み書きしようとした際の競合状態を防ぎます。
  3. Counter.String() メソッドと Counter.ServeHTTP() メソッドでのミューテックスの使用:

    • ctr.mu.Lock()defer ctr.mu.Unlock() が各メソッドの冒頭に追加されました。
    • Lock() は、共有リソース(この場合は ctr.n)へのアクセスを開始する前にミューテックスをロックします。
    • defer ctr.mu.Unlock() は、メソッドが終了する際に(正常終了、エラー終了に関わらず)ミューテックスをアンロックすることを保証します。これにより、n の読み取りや更新操作がアトミック(不可分)になり、並行処理安全性が確保されます。
  4. webroot 変数のデフォルト値の変更:

    • var webroot = flag.String("root", os.Getenv("HOME"), "web root directory")
    • 以前はハードコードされたパス(/home/rsc)でしたが、os.Getenv("HOME") を使用することで、実行環境のユーザーのホームディレクトリをデフォルト値として設定するようになりました。これにより、サンプルコードの移植性が向上します。
  5. http.Handle から http.HandleFunc への移行:

    • main 関数内で、FlagServer, ArgServer, HelloServer, DateServer といったハンドラ関数の登録に http.Handle("/path", http.HandlerFunc(Handler)) の形式ではなく、より簡潔な http.HandleFunc("/path", Handler) の形式が使用されるようになりました。
    • これは機能的な変更ではなく、Goの慣用的な書き方への「現代化」であり、コードの可読性と記述性を向上させます。

関連リンク

参考にした情報源リンク

  • Go言語公式ドキュメント (上記「関連リンク」に記載の各パッケージのドキュメント)
  • Go言語の並行処理に関する一般的な情報源 (例: Go Concurrency Patterns, Effective Goなど)
  • Go言語のミューテックスに関する解説記事 (例: A Tour of Go - Mutexes)
  • Go言語のHTTPハンドラに関する解説記事 (例: Writing HTTP servers in Go)