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

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

このコミットは、Go言語の標準ライブラリである net/http パッケージ内の server.go ファイルに対する変更です。具体的には、HTTP接続を処理する Serve 関数および ListenAndServe 関数に関するコメントの記述を修正し、内部で「サービススレッド」ではなく「サービスゴルーチン」が生成されることを明確にしています。これは、Goの並行処理モデルにおけるゴルーチンの役割を正確に反映するためのドキュメント上の修正であり、コードの動作自体を変更するものではありません。

コミット

commit 1a03580ef112d7e7103790e102a5582b8dcbaf0d
Author: Matthew Dempsky <mdempsky@google.com>
Date:   Wed Jan 16 14:05:41 2013 -0800

    net/http: Serve creates service goroutines, not service threads
    
    R=bradfitz
    CC=golang-dev
    https://golang.org/cl/7132045

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

https://github.com/golang/go/commit/1a03580ef112d7e7103790e102a5582b8dcbaf0d

元コミット内容

net/http: Serve creates service goroutines, not service threads

変更の背景

このコミットの背景には、Go言語の並行処理モデルにおける「ゴルーチン (goroutine)」と従来のオペレーティングシステム(OS)の「スレッド (thread)」との違いに対する理解の深化と、それに基づくドキュメントの正確性の追求があります。

Go言語は、軽量な並行処理のプリミティブとしてゴルーチンを提供しています。初期のGoのドキュメントや内部コメントでは、ゴルーチンがOSスレッド上で実行されるという事実から、便宜的に「スレッド」という言葉が使われることがありました。しかし、ゴルーチンはOSスレッドよりもはるかに軽量であり、Goランタイムが多数のゴルーチンを少数のOSスレッドに多重化してスケジューリングします。このため、ゴルーチンはOSスレッドとは異なる抽象化レベルにあり、その特性も大きく異なります。

net/http パッケージの Serve 関数は、新しいHTTP接続が確立されるたびに、その接続を処理するための新しい並行実行単位を起動します。Goの設計思想から、これは当然ゴルーチンとして実装されています。しかし、以前のコメントでは「サービススレッド」という表現が使われており、これはGoの並行処理モデルを正確に反映していませんでした。

このコミットは、この誤解を招く可能性のある記述を修正し、Goの並行処理の核心であるゴルーチンという用語に統一することで、ドキュメントの正確性と一貫性を向上させることを目的としています。これにより、Goのコードを理解しようとする開発者が、Goの並行処理モデルについてより正確な知識を得られるようになります。

前提知識の解説

1. Go言語の並行処理モデル

Go言語は、並行処理を言語レベルでサポートしており、その中心となるのが「ゴルーチン (goroutine)」と「チャネル (channel)」です。

  • ゴルーチン (Goroutine):

    • Goランタイムによって管理される軽量な実行単位です。OSスレッドよりもはるかに少ないメモリ(初期スタックサイズは数KB程度)で起動でき、数百万個のゴルーチンを同時に実行することも可能です。
    • Goランタイムのスケジューラが、少数のOSスレッド(通常はCPUコア数と同程度)に多数のゴルーチンを多重化して実行します。これにより、OSスレッドの切り替えコストを抑えつつ、高い並行性を実現します。
    • go キーワードを関数の前に置くだけで簡単に起動できます。例: go myFunction()
    • ゴルーチン間の通信には、主にチャネルを使用します。
  • チャネル (Channel):

    • ゴルーチン間で安全にデータを送受信するための通信メカニズムです。
    • チャネルは、データの送受信を同期させる役割も果たし、競合状態(race condition)の発生を防ぎます。
    • 「Do not communicate by sharing memory; instead, share memory by communicating.(メモリを共有して通信するのではなく、通信によってメモリを共有せよ)」というGoの並行処理の哲学を体現しています。

2. OSスレッド (Operating System Thread)

  • OSによって管理される実行単位で、プロセス内で独立した実行パスを持ちます。
  • 各スレッドは独自のスタック、レジスタセットを持ちますが、同じプロセスの他のスレッドとヒープメモリなどのリソースを共有します。
  • スレッドの生成やコンテキストスイッチのコストは、ゴルーチンに比べてはるかに大きいです。
  • OSスレッドの数は、システムリソースによって制限されます。

3. net/http パッケージ

Goの標準ライブラリに含まれる net/http パッケージは、HTTPクライアントとサーバーを簡単に構築するための機能を提供します。

  • http.Server: HTTPサーバーの振る舞いを定義する構造体です。
  • http.ListenAndServe(addr string, handler Handler): 指定されたアドレスでHTTPサーバーを起動し、リクエストを処理します。内部的には http.Server を使用します。
  • http.Serve(l net.Listener, handler Handler): 既存の net.Listener からの接続を受け入れ、各接続に対してハンドラを呼び出して処理します。

これらの関数は、新しいHTTP接続が来るたびに、その接続を独立して処理するために新しい並行実行単位を起動します。Goの設計上、これはゴルーチンとして実装されます。

技術的詳細

このコミットは、Goの net/http パッケージにおける Serve 関数および ListenAndServe 関数が、新しいHTTP接続を処理する際にどのように並行処理を実現しているかについてのドキュメント上の記述を修正しています。

Goの net/http サーバーは、net.Listener を介して新しいTCP接続を受け入れると、その接続ごとに新しいゴルーチンを起動します。このゴルーチン内で、HTTPリクエストの読み込み、ハンドラ関数の呼び出し、レスポンスの書き込みといった一連の処理が行われます。

従来のOSスレッドベースのサーバーでは、新しい接続ごとに新しいOSスレッドを生成することが一般的でした。しかし、OSスレッドはリソース消費が大きく、数が増えるとコンテキストスイッチのオーバーヘッドが増大し、パフォーマンスが低下する可能性があります。

Goのゴルーチンは、この問題を解決するために設計されました。ゴルーチンは非常に軽量であるため、数万、数十万といった多数の同時接続を効率的に処理できます。Goランタイムのスケジューラが、これらの多数のゴルーチンを、利用可能なCPUコア数に応じた少数のOSスレッドにマッピングし、効率的に実行します。これにより、開発者は並行処理を意識することなく、あたかも各接続が独立した「サービススレッド」で処理されているかのようにコードを書くことができますが、実際の内部実装はより効率的なゴルーチンベースとなっています。

このコミットは、この内部実装の正確な用語をドキュメントに反映させることで、Goの並行処理モデルに対する誤解を防ぎ、より正確な情報を提供することを目的としています。

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

変更は src/pkg/net/http/server.go ファイルの2箇所にあります。

  1. Serve 関数のコメント(func Serve(l net.Listener, handler Handler) error の上)
  2. Server 型の Serve メソッドのコメント(func (srv *Server) Serve(l net.Listener) error の上)

具体的には、以下の行が変更されました。

--- a/src/pkg/net/http/server.go
+++ b/src/pkg/net/http/server.go
@@ -1216,7 +1216,7 @@ func HandleFunc(pattern string, handler func(ResponseWriter, *Request)) {
 }
 
 // Serve accepts incoming HTTP connections on the listener l,
-// creating a new service thread for each.  The service threads
+// creating a new service goroutine for each.  The service goroutines
 // read requests and then call handler to reply to them.
 // Handler is typically nil, in which case the DefaultServeMux is used.
 func Serve(l net.Listener, handler Handler) error {
@@ -1250,7 +1250,7 @@ func (srv *Server) ListenAndServe() error {
 }
 
 // Serve accepts incoming connections on the Listener l, creating a
-// new service thread for each.  The service threads read requests and
+// new service goroutine for each.  The service goroutines read requests and
 // then call srv.Handler to reply to them.
 func (srv *Server) Serve(l net.Listener) error {
 	defer l.Close()

コアとなるコードの解説

変更されたのは、Serve 関数と Server.Serve メソッドのドキュメンテーションコメントです。

  • 変更前:

    // creating a new service thread for each.  The service threads
    

    この記述は、HTTP接続ごとに新しい「サービススレッド」が作成されると説明していました。

  • 変更後:

    // creating a new service goroutine for each.  The service goroutines
    

    この記述は、「サービススレッド」を「サービスゴルーチン」に修正しています。

この変更は、Goの並行処理モデルの正確な用語を使用することで、ドキュメントの整合性を高めています。Goの net/http パッケージは、実際に各接続を処理するために新しいゴルーチンを起動します。これはGoのランタイムによって効率的にスケジューリングされ、OSスレッドの直接的な管理とは異なります。したがって、このコメントの修正は、Goの設計思想と実装をより正確に反映したものです。

コードの動作自体には影響はなく、純粋にドキュメンテーションの改善です。

関連リンク

参考にした情報源リンク

  • Go言語の公式ドキュメントおよびパッケージドキュメント
  • Go言語の並行処理に関する一般的な解説記事
  • GitHub上のGoリポジトリのコミット履歴と関連する議論 (CL/7132045)
  • GoのゴルーチンとOSスレッドに関する技術ブログやフォーラムの議論

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

このコミットは、Go言語の標準ライブラリである net/http パッケージ内の server.go ファイルに対するドキュメンテーションの修正です。具体的には、HTTP接続を処理する Serve 関数および ListenAndServe 関数に関するコメントの記述を修正し、内部で「サービススレッド」ではなく「サービスゴルーチン」が生成されることを明確にしています。これは、Goの並行処理モデルにおけるゴルーチンの役割を正確に反映するためのドキュメント上の修正であり、コードの動作自体を変更するものではありません。

コミット

commit 1a03580ef112d7e7103790e102a5582b8dcbaf0d
Author: Matthew Dempsky <mdempsky@google.com>
Date:   Wed Jan 16 14:05:41 2013 -0800

    net/http: Serve creates service goroutines, not service threads
    
    R=bradfitz
    CC=golang-dev
    https://golang.org/cl/7132045

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

https://github.com/golang/go/commit/1a03580ef112d7e7103790e102a5582b8dcbaf0d

元コミット内容

net/http: Serve creates service goroutines, not service threads

変更の背景

このコミットの背景には、Go言語の並行処理モデルにおける「ゴルーチン (goroutine)」と従来のオペレーティングシステム(OS)の「スレッド (thread)」との違いに対する理解の深化と、それに基づくドキュメントの正確性の追求があります。

Go言語は、軽量な並行処理のプリミティブとしてゴルーチンを提供しています。初期のGoのドキュメントや内部コメントでは、ゴルーチンがOSスレッド上で実行されるという事実から、便宜的に「スレッド」という言葉が使われることがありました。しかし、ゴルーチンはOSスレッドよりもはるかに軽量であり、Goランタイムが多数のゴルーチンを少数のOSスレッドに多重化してスケジューリングします。このため、ゴルーチンはOSスレッドとは異なる抽象化レベルにあり、その特性も大きく異なります。

net/http パッケージの Serve 関数は、新しいHTTP接続が確立されるたびに、その接続を処理するための新しい並行実行単位を起動します。Goの設計思想から、これは当然ゴルーチンとして実装されています。しかし、以前のコメントでは「サービススレッド」という表現が使われており、これはGoの並行処理モデルを正確に反映していませんでした。

このコミットは、この誤解を招く可能性のある記述を修正し、Goの並行処理の核心であるゴルーチンという用語に統一することで、ドキュメントの正確性と一貫性を向上させることを目的としています。これにより、Goのコードを理解しようとする開発者が、Goの並行処理モデルについてより正確な知識を得られるようになります。

前提知識の解説

1. Go言語の並行処理モデル

Go言語は、並行処理を言語レベルでサポートしており、その中心となるのが「ゴルーチン (goroutine)」と「チャネル (channel)」です。

  • ゴルーチン (Goroutine):

    • Goランタイムによって管理される軽量な実行単位です。OSスレッドよりもはるかに少ないメモリ(初期スタックサイズは数KB程度)で起動でき、数百万個のゴルーチンを同時に実行することも可能です。
    • Goランタイムのスケジューラが、少数のOSスレッド(通常はCPUコア数と同程度)に多数のゴルーチンを多重化して実行します(M:Nスケジューリングモデル)。これにより、OSスレッドの切り替えコストを抑えつつ、高い並行性を実現します。
    • go キーワードを関数の前に置くだけで簡単に起動できます。例: go myFunction()
    • ゴルーチン間の通信には、主にチャネルを使用します。
  • チャネル (Channel):

    • ゴルーチン間で安全にデータを送受信するための通信メカニズムです。
    • チャネルは、データの送受信を同期させる役割も果たし、競合状態(race condition)の発生を防ぎます。
    • 「Do not communicate by sharing memory; instead, share memory by communicating.(メモリを共有して通信するのではなく、通信によってメモリを共有せよ)」というGoの並行処理の哲学を体現しています。

2. OSスレッド (Operating System Thread)

  • OSによって管理される実行単位で、プロセス内で独立した実行パスを持ちます。
  • 各スレッドは独自のスタック、レジスタセットを持ちますが、同じプロセスの他のスレッドとヒープメモリなどのリソースを共有します。
  • スレッドの生成やコンテキストスイッチのコストは、ゴルーチンに比べてはるかに大きいです。OSカーネルによるコンテキストスイッチは、レジスタの保存と復元を伴うため、比較的コストの高い操作です。
  • OSスレッドの数は、システムリソースによって制限されます。

3. net/http パッケージ

Goの標準ライブラリに含まれる net/http パッケージは、HTTPクライアントとサーバーを簡単に構築するための機能を提供します。

  • http.Server: HTTPサーバーの振る舞いを定義する構造体です。
  • http.ListenAndServe(addr string, handler Handler): 指定されたアドレスでHTTPサーバーを起動し、リクエストを処理します。内部的には http.Server を使用します。
  • http.Serve(l net.Listener, handler Handler): 既存の net.Listener からの接続を受け入れ、各接続に対してハンドラを呼び出して処理します。

これらの関数は、新しいHTTP接続が来るたびに、その接続を独立して処理するために新しい並行実行単位を起動します。Goの設計上、これはゴルーチンとして実装されます。

技術的詳細

このコミットは、Goの net/http パッケージにおける Serve 関数および ListenAndServe 関数が、新しいHTTP接続を処理する際にどのように並行処理を実現しているかについてのドキュメント上の記述を修正しています。

Goの net/http サーバーは、net.Listener を介して新しいTCP接続を受け入れると、その接続ごとに新しいゴルーチンを起動します。このゴルーチン内で、HTTPリクエストの読み込み、ハンドラ関数の呼び出し、レスポンスの書き込みといった一連の処理が行われます。

従来のOSスレッドベースのサーバーでは、新しい接続ごとに新しいOSスレッドを生成することが一般的でした。しかし、OSスレッドはリソース消費が大きく、数が増えるとコンテキストスイッチのオーバーヘッドが増大し、パフォーマンスが低下する可能性があります。

Goのゴルーチンは、この問題を解決するために設計されました。ゴルーチンは非常に軽量であるため、数万、数十万といった多数の同時接続を効率的に処理できます。Goランタイムのスケジューラが、これらの多数のゴルーチンを、利用可能なCPUコア数に応じた少数のOSスレッドにマッピングし、効率的に実行します。ゴルーチンのスケジューリングはユーザー空間で行われるため、OSカーネルの介入が不要であり、コンテキストスイッチの効率が大幅に向上します。また、ゴルーチンがI/Oなどでブロックされた場合でも、Goランタイムは同じOSスレッド上で別のゴルーチンを割り当てることができ、スレッドがアイドル状態になるのを防ぎます。これにより、開発者は並行処理を意識することなく、あたかも各接続が独立した「サービススレッド」で処理されているかのようにコードを書くことができますが、実際の内部実装はより効率的なゴルーチンベースとなっています。

このコミットは、この内部実装の正確な用語をドキュメントに反映させることで、Goの並行処理モデルに対する誤解を防ぎ、より正確な情報を提供することを目的としています。

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

変更は src/pkg/net/http/server.go ファイルの2箇所にあります。

  1. Serve 関数のコメント(func Serve(l net.Listener, handler Handler) error の上)
  2. Server 型の Serve メソッドのコメント(func (srv *Server) Serve(l net.Listener) error の上)

具体的には、以下の行が変更されました。

--- a/src/pkg/net/http/server.go
+++ b/src/pkg/net/http/server.go
@@ -1216,7 +1216,7 @@ func HandleFunc(pattern string, handler func(ResponseWriter, *Request)) {
 }
 
 // Serve accepts incoming HTTP connections on the listener l,
-// creating a new service thread for each.  The service threads
+// creating a new service goroutine for each.  The service goroutines
 // read requests and then call handler to reply to them.
 // Handler is typically nil, in which case the DefaultServeMux is used.
 func Serve(l net.Listener, handler Handler) error {
@@ -1250,7 +1250,7 @@ func (srv *Server) ListenAndServe() error {
 }
 
 // Serve accepts incoming connections on the Listener l, creating a
-// new service thread for each.  The service threads read requests and
+// new service goroutine for each.  The service goroutines read requests and
 // then call srv.Handler to reply to them.
 func (srv *Server) Serve(l net.Listener) error {
 	defer l.Close()

コアとなるコードの解説

変更されたのは、Serve 関数と Server.Serve メソッドのドキュメンテーションコメントです。

  • 変更前:

    // creating a new service thread for each.  The service threads
    

    この記述は、HTTP接続ごとに新しい「サービススレッド」が作成されると説明していました。

  • 変更後:

    // creating a new service goroutine for each.  The service goroutines
    

    この記述は、「サービススレッド」を「サービスゴルーチン」に修正しています。

この変更は、Goの並行処理モデルの正確な用語を使用することで、ドキュメントの整合性を高めています。Goの net/http パッケージは、実際に各接続を処理するために新しいゴルーチンを起動します。これはGoのランタイムによって効率的にスケジューリングされ、OSスレッドの直接的な管理とは異なります。したがって、このコメントの修正は、Goの設計思想と実装をより正確に反映したものです。

コードの動作自体には影響はなく、純粋にドキュメンテーションの改善です。

関連リンク

参考にした情報源リンク

  • Go言語の公式ドキュメントおよびパッケージドキュメント
  • Go言語の並行処理に関する一般的な解説記事
  • GitHub上のGoリポジトリのコミット履歴と関連する議論 (CL/7132045)
  • GoのゴルーチンとOSスレッドに関する技術ブログやフォーラムの議論
  • Web検索結果: "Go net/http Serve goroutine vs thread" (medium.com, dev.to, stackoverflow.com, geeksforgeeks.org など)